Plan catalogue
The plans on sale, with the entitlements each one grants. It is a catalogue for building a comparison — what YOUR workspace is on, and how much of it you have used, is a different pair of endpoints.
The catalogue
One route, no pagination, no filters. It lists the ACTIVE plans; retired ones are excluded here rather than deleted, because they keep their subscribers. For the plan in force and its usage, see plans and quotas.
This is not a public route
It requires workspace.read, and that requirement is what makes the guard resolve a tenant — so a signed-out pricing page cannot call it and would get a 401 where a price belongs. Both credentials work once a workspace exists.
A catalogue entry
| Field | Type | Description |
|---|---|---|
| id | string | The plan's public id. |
| code | string | The stable code. This is what other endpoints name a plan by. |
| version | number | Plans are versioned; a subscriber stays on the version they hold. |
| name, description | string | null | The display name and an optional description. |
| price_amount_minor | number | null | A minor-unit integer, or null for NOT PRICED. |
| currency | string | null | The currency the price is in. Null when unpriced. |
| billing_interval | string | null | How often it is charged. Null when unpriced. |
| entitlements | array | What the plan grants. See below. |
# NOT public — the caller must already have a workspace.
curl https://whats.azzamkh.sa/api/v1/plans \
-H "Authorization: Bearer $WA_API_KEY"
# 200 OK
# {
# "data": [
# {
# "id": "pln_...",
# "code": "standard",
# "version": 3,
# "name": "Standard",
# "description": null,
# "price_amount_minor": null,
# "currency": null,
# "billing_interval": null,
# "entitlements": [
# { "key": "monthly_message_quota", "type": "number",
# "unlimited": false, "value": 10000 },
# { "key": "campaigns_enabled", "type": "boolean",
# "unlimited": false, "value": true }
# ]
# }
# ]
# }Entitlements
Each entry carries a key, a type, an unlimited flag and a value. The same vocabulary appears on the current plan, where it is joined by what has been consumed.
unlimitedis a STATE, never a sentinel value like -1. A number entitlement is either unlimited or carries a value.- Unlimited is NOT the same as an absent key. An unlimited row says the plan decided this is uncapped; an absent key says the plan does not cap this at all.
- A boolean entitlement is a feature switch. An ABSENT one means allowed — only an explicit
falseforbids. - Entitlements belong to a plan VERSION. Comparing two plans means comparing the versions you were given, not a code.
Pricing
Every plan is unpriced today
price_amount_minor, currency and billing_interval are all null on every plan the catalogue currently returns, because pricing is an open commercial decision. Render that as not priced — never as free, and never as a placeholder number. Billing explains what that does to a total.
There is no endpoint that changes a workspace's plan. A plan change is arranged with the operator and applied by them, either immediately or at the end of the period.