Swarmz

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.

POSThttps://api.swarmz.net/functions/v1/platform-plan

Changes 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_code present — the real upgrade/downgrade path. The named plan is resolved (scoped to your account) and platform_assign_plan rewrites the tenant's entitlements from the plan template and applies them to billing. This is how the WHMCS module changes a plan.
  • plan_code absent — a legacy raw-entitlements replace, for hosts that drive caps directly. Prefer plan_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
}
FieldTypeDescription
okbooleanAlways true on success.
mode'upgrade' | 'downgrade' | nullEcho of the mode hint you sent; null if you sent none or an unrecognized value.
successbooleanplan_code path only. From platform_assign_plantrue on a successful assign.
entitlementsobjectplan_code path only. The caps the plan applied to the tenant.

Errors

StatuserrorreasonWhen
400missing_fieldstenant_id or external_ref requiredNeither identifier supplied
400missing_fieldsplan_code or entitlements requiredNo plan_code and the entitlements object is empty after sanitization
401unauthorizedmissing_bearer / invalid_key / account_disabledSee Authentication
404tenant_not_foundNo tenant matched the identifier under your account
405method_not_allowedAnything other than POST
409suspendedThe tenant is suspended — unsuspend before changing the plan
410terminatedThe tenant has been terminated
422plan_not_foundthe plan_codeplan_code did not resolve to a plan under your account
422assign_failedstructured 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
429rate_limitedper_key / per_ipRate limit hit — see Rate limits
500update_failedRPC messageplatform_plan_lookup, platform_assign_plan, or platform_update_entitlements failed server-side; safe to retry
500internal_errorUnhandled 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_id from create; external_ref is the fallback.
  • mode is 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 by platform_assign_plan and 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_name is 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.

On this page