Skip to content
Contents
Usage and account

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

GET/v1/plans

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

FieldTypeDescription
idstringThe plan's public id.
codestringThe stable code. This is what other endpoints name a plan by.
versionnumberPlans are versioned; a subscriber stays on the version they hold.
name, descriptionstring | nullThe display name and an optional description.
price_amount_minornumber | nullA minor-unit integer, or null for NOT PRICED.
currencystring | nullThe currency the price is in. Null when unpriced.
billing_intervalstring | nullHow often it is charged. Null when unpriced.
entitlementsarrayWhat the plan grants. See below.
curl
# 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.

  • unlimited is 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 false forbids.
  • 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.