Change a tenant's plan
Move a tenant onto a different named plan, or patch its raw entitlements. Maps to WHMCS ChangePackage. Addressed by tenant_id.
https://api.swarmz.net/functions/v1/platform-planChanges an existing tenant's plan. Maps to WHMCS ChangePackage (upgrade or downgrade). Address the tenant by tenant_id — the id returned by create — or fall back to your external_ref. Supply exactly one identifier.
The endpoint has two paths, selected by the body:
plan_codepresent — the real upgrade/downgrade path. The named plan is resolved (scoped to your account) andplatform_assign_planrewrites the tenant's entitlements from the plan template and applies them to billing. This is how the WHMCS module changes a plan.plan_codeabsent — a legacy raw-entitlementsreplace, for hosts that drive caps directly. Preferplan_code.
Use plan_code, not raw entitlements
ChangePackage sends plan_code. The raw entitlements object is the legacy
path and is no longer used by the module. See
Entitlements for what a plan's knobs mean.
Parameters
Prop
Type
Request
curl -X POST https://api.swarmz.net/functions/v1/platform-plan \
-H "Authorization: Bearer sk_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "aa7d7fae-0386-4d8a-b977-72f856eac242",
"plan_code": "scale-monthly",
"mode": "upgrade"
}'const res = await fetch('https://api.swarmz.net/functions/v1/platform-plan', {
method: 'POST',
headers: {
Authorization: 'Bearer sk_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6',
'Content-Type': 'application/json',
},
body: JSON.stringify({
tenant_id: 'aa7d7fae-0386-4d8a-b977-72f856eac242',
plan_code: 'scale-monthly',
mode: 'upgrade',
}),
});
const data = await res.json();import requests
res = requests.post(
"https://api.swarmz.net/functions/v1/platform-plan",
headers={"Authorization": "Bearer sk_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"},
json={
"tenant_id": "aa7d7fae-0386-4d8a-b977-72f856eac242",
"plan_code": "scale-monthly",
"mode": "upgrade",
},
)
data = res.json()Response
On the plan_code path the body is { ok, mode, …assignResult } — the result of platform_assign_plan spread at the top level:
{
"ok": true,
"mode": "upgrade",
"success": true,
"entitlements": { "...": "..." }
}On the legacy entitlements path the body is minimal:
{
"ok": true,
"mode": null
}| Field | Type | Description |
|---|---|---|
ok | boolean | Always true on success. |
mode | 'upgrade' | 'downgrade' | null | Echo of the mode hint you sent; null if you sent none or an unrecognized value. |
success | boolean | plan_code path only. From platform_assign_plan — true on a successful assign. |
entitlements | object | plan_code path only. The caps the plan applied to the tenant. |
Errors
| Status | error | reason | When |
|---|---|---|---|
| 400 | missing_fields | tenant_id or external_ref required | Neither identifier supplied |
| 400 | missing_fields | plan_code or entitlements required | No plan_code and the entitlements object is empty after sanitization |
| 401 | unauthorized | missing_bearer / invalid_key / account_disabled | See Authentication |
| 404 | tenant_not_found | — | No tenant matched the identifier under your account |
| 405 | method_not_allowed | — | Anything other than POST |
| 409 | suspended | — | The tenant is suspended — unsuspend before changing the plan |
| 410 | terminated | — | The tenant has been terminated |
| 422 | plan_not_found | the plan_code | plan_code did not resolve to a plan under your account |
| 422 | assign_failed | structured fields from the RPC (e.g. plan_account_mismatch, not_platform_workspace) | The plan resolved but platform_assign_plan rejected it; the RPC's own fields are spread into the response alongside error |
| 429 | rate_limited | per_key / per_ip | Rate limit hit — see Rate limits |
| 500 | update_failed | RPC message | platform_plan_lookup, platform_assign_plan, or platform_update_entitlements failed server-side; safe to retry |
| 500 | internal_error | — | Unhandled server error; safe to retry |
Idempotency
No idempotency key — but assigning the same plan is safe
platform-plan has no idempotency key. Re-sending the same plan_code
re-applies the same plan template, landing the tenant in the same final state.
On the legacy entitlements path the replace is also naturally idempotent for an
unchanged object. See Idempotency.
Notes
- Address the tenant by
tenant_idfromcreate;external_refis the fallback. modeis observability only. It is written to the lifecycle log and echoed back; it never alters credit grants, rollover, or the cycle. Credit behavior is owned byplatform_assign_planand the monthly cycle (plan-refresh).- The plan is resolved against your account only, so you can never assign another reseller's plan.
- On the legacy entitlements path the entitlements JSONB is replaced within the keys you send. Because the named-plan path rewrites entitlements wholesale, the customer-facing
plan_nameis carried by the plan definition (or must be re-sent on a raw call) — see Entitlements. - Changing the plan does not reset the credit cycle on its own. If your billing flow renews credits on a plan change, follow with
plan-refresh.
Initial prompt
Capture the first build prompt on your own storefront and have the customer's workspace open mid-build on their first login. Works through the WHMCS module's Prompt Box or a single field on platform-create.
Refresh a tenant's billing cycle
Rollover unused credits and reset the monthly allowance at a billing-cycle boundary. Idempotent per (tenant, cycle anchor).