Skip to content

Admin

List Admins

List all platform admins, joined to user + adder info.

Responses

StatusDescriptionBody
200Successful ResponsePlatformAdminResponse[]
422Validation ErrorHTTPValidationError

Add Admin

Grant platform-admin access to an existing, already-registered user.

Request body (required)

FieldTypeRequiredDescription
emailstringyes

Responses

StatusDescriptionBody
201Successful ResponsePlatformAdminResponse
422Validation ErrorHTTPValidationError

Remove Admin

Revoke platform-admin access. Guards: no self-removal, no removing the last admin.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yes

Responses

StatusDescriptionBody
204Successful Response
422Validation ErrorHTTPValidationError

List Allowed Emails

List all registration-allowlist entries.

Responses

StatusDescriptionBody
200Successful ResponseAllowedEmailResponse[]
422Validation ErrorHTTPValidationError

Add Allowed Email

Add an entry to the registration allowlist.

Request body (required)

FieldTypeRequiredDescription
emailstringyes

Responses

StatusDescriptionBody
201Successful ResponseAllowedEmailResponse
422Validation ErrorHTTPValidationError

DELETE /api/admin/allowed-emails/{allowed_email_id}

Section titled “DELETE /api/admin/allowed-emails/{allowed_email_id}”

Remove Allowed Email

Remove a registration-allowlist entry.

Parameters

NameInTypeRequiredDescription
allowed_email_idpathstring (uuid)yes

Responses

StatusDescriptionBody
204Successful Response
422Validation ErrorHTTPValidationError

List Orgs

List/search all organizations, cross-tenant, with per-org member counts, plan, suspension state and remaining credits.

Subscription and balance are LEFT joins on purpose: an org with neither is exactly the state worth spotting from the list (no subscription = every entitlement denied), so it stays a row with null plan rather than vanishing. credits_remaining is the gate’s own expression — see OrgListItem. It folds the overdraft grace in, so credits_balance (owned) and credits_overdraft (lent) ride alongside it: a single figure that mixes the two reads as more credits than the org has.

Parameters

NameInTypeRequiredDescription
limitqueryintegerno
offsetqueryintegerno
plan_codequerystringnoOnly orgs on this plan. Pass __none__ for orgs with no subscription row at all — every entitlement is denied for those, and they need a backfill.
qquerystringnoSearch org name/slug (substring, case-insensitive)
suspendedquerybooleannotrue = only suspended orgs, false = only active. Omit for both.

Responses

StatusDescriptionBody
200Successful ResponseOrgListResponse
422Validation ErrorHTTPValidationError

Get Org

Org detail plus its member roster.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseOrgDetailResponse
422Validation ErrorHTTPValidationError

Get Org Audit

Change history for one org — who changed what, when.

Merges the two trails that can say something about an org, newest first:

  • admin_audit_log — actions taken through this admin surface, plus webhook-driven plan changes recorded with a system actor;
  • audit_log — Layer-2 semantic business events service code records against the org (token.revoked, …).

Deliberately not included: Layer-1 raw row capture (audit.record_version). It has no org column to scope by and one page-load of agent traffic would bury every deliberate change in row diffs.

The merge happens in Python rather than SQL: the two tables share no column names or types, and both are small enough here that over-fetching limit + offset from each is cheaper than the UNION plumbing.

Parameters

NameInTypeRequiredDescription
limitqueryintegerno
offsetqueryintegerno
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseOrgAuditResponse
422Validation ErrorHTTPValidationError

Add Org Member

Add an EXISTING, already-registered user to an org. Never creates users or sends invites.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes

Request body (required)

FieldTypeRequiredDescription
emailstringyes
rolestringno

Responses

StatusDescriptionBody
201Successful ResponseOrgMemberInfo
422Validation ErrorHTTPValidationError

PATCH /api/admin/orgs/{org_id}/members/{user_id}

Section titled “PATCH /api/admin/orgs/{org_id}/members/{user_id}”

Update Org Member Role

Change a member’s role. Refuses if it would leave the org with 0 owners.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes
user_idpathstring (uuid)yes

Request body (required)

FieldTypeRequiredDescription
rolestringyes

Responses

StatusDescriptionBody
200Successful ResponseOrgMemberInfo
422Validation ErrorHTTPValidationError

DELETE /api/admin/orgs/{org_id}/members/{user_id}

Section titled “DELETE /api/admin/orgs/{org_id}/members/{user_id}”

Remove Org Member

Remove a member from an org. Refuses to remove the last OWNER.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes
user_idpathstring (uuid)yes

Responses

StatusDescriptionBody
204Successful Response
422Validation ErrorHTTPValidationError

Suspend Org

Suspend an organization — blocks regular member access via get_current_org.

Idempotent: calling this on an already-suspended org is a no-op (keeps the original suspended_at timestamp, 200, no duplicate audit entry) rather than 409 — this is a “make it so” action, safe to double-click from the UI.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseOrgSuspensionResponse
422Validation ErrorHTTPValidationError

Unsuspend Org

Lift a suspension. Idempotent no-op if the org isn’t currently suspended.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseOrgSuspensionResponse
422Validation ErrorHTTPValidationError

Get Org Usage

Usage summary for one org — same shape/service as the self-serve /api/orgs/{org_id}/usage/summary endpoint, just without membership gating.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes
windowquerystringnoCounter/cost window: 24h, 7d, 30d (default 30d)

Responses

StatusDescriptionBody
200Successful ResponseUsageSummaryResponse
422Validation ErrorHTTPValidationError

Backfill Subscriptions

Give every org that lacks an org_subscriptions row one. Re-runnable.

The repair path for a window that already happened: orgs created between the plans/org_subscriptions migration (which backfilled everyone alive at the time) and the org-creation hook have no subscription, so they resolve to UNSUBSCRIBED — every feature denied, every count cap 0. Creation now subscribes in the same transaction as the org insert, but that fixes nothing retroactively.

Each org subscribed here also receives the plan’s credit grant and overdraft floor, in the same transaction — a subscription without them is an org that cannot do any work (plan §4.4). The default plan (enterprise) allocates nothing, so the usual repair run writes no ledger rows.

Idempotent, and cheap to run blind: the underlying INSERT ... SELECT only sees orgs with no subscription row, so a second call inserts nothing, returns created: 0 and writes no audit entry. The grant carries its own once-ever-per-org-per-plan key on top, so it cannot double-allocate even if the subscription row is later recreated. It never modifies an existing subscription — a plan change is a separate, deliberate admin action, not something a repair sweep should do by accident. That makes this safe to run after any deploy, and safe to double-click.

It is not a way to top an org up: an org that already has a subscription gets nothing here, and one whose grant went missing is repaired with an adjustment.

Request body (required)

FieldTypeRequiredDescription
org_idstring (uuid)noRepair only this organization. Omit to repair every organization that has no subscription row.
plan_codestringnoPlan to place missing subscriptions on. Defaults to CREDITS_DEFAULT_NEW_ORG_PLAN — the same plan org creation uses — so a repaired org is indistinguishable from a freshly created one.

Responses

StatusDescriptionBody
200Successful ResponseSubscriptionBackfillResponse
422Validation ErrorHTTPValidationError

POST /api/admin/users/{user_id}/deactivate

Section titled “POST /api/admin/users/{user_id}/deactivate”

Deactivate User

Disable a user’s account. Guard: no self-deactivate.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseUserStatusResponse
422Validation ErrorHTTPValidationError

POST /api/admin/users/{user_id}/reactivate

Section titled “POST /api/admin/users/{user_id}/reactivate”

Reactivate User

Re-enable a previously deactivated user’s account.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseUserStatusResponse
422Validation ErrorHTTPValidationError

List Waitlist

List waitlist signups, most recently asked first.

Parameters

NameInTypeRequiredDescription
limitqueryintegerno
statusqueryWaitlistStatusnoOnly rows in this status. Omit for all.

Responses

StatusDescriptionBody
200Successful ResponseWaitlistListResponse
422Validation ErrorHTTPValidationError

POST /api/admin/waitlist/{waitlist_id}/dismiss

Section titled “POST /api/admin/waitlist/{waitlist_id}/dismiss”

Dismiss From Waitlist

Decline a waitlist signup.

Keeps the row. Deleting it would let the same address walk straight back into the pending queue on its next submit, and the decision would have to be taken again every time.

Parameters

NameInTypeRequiredDescription
waitlist_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseWaitlistEntryResponse
422Validation ErrorHTTPValidationError

POST /api/admin/waitlist/{waitlist_id}/invite

Section titled “POST /api/admin/waitlist/{waitlist_id}/invite”

Invite From Waitlist

Admit somebody from the waitlist and tell them so.

Parameters

NameInTypeRequiredDescription
waitlist_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseWaitlistEntryResponse
422Validation ErrorHTTPValidationError

POST /api/admin/waitlist/{waitlist_id}/resend

Section titled “POST /api/admin/waitlist/{waitlist_id}/resend”

Resend Waitlist Invite

Mail an already-admitted person again.

For the row whose admission stuck but whose mail did not. Refuses on a row that was never admitted — sending “your invitation is ready” to somebody who is still refused at the door is worse than sending nothing.

Parameters

NameInTypeRequiredDescription
waitlist_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseWaitlistEntryResponse
422Validation ErrorHTTPValidationError