Management API
Everything the workspace can do, your servers can do too. One route table, JSON in and out, credentials scoped to exactly what they should touch.
Authentication
Scoped management credentials: explicit scopes, application or organization binding, product confinement, rotation with grace.
Create a credential on the workspace's API page and send it as a bearer token. A credential is bound either to one application and environment or to the whole organization; it holds explicit scopes, may be confined to some products, and rotates with a grace period so the old secret keeps working while you roll the new one out. Routes about people and credentials themselves are never available to a credential; those need a signed-in member.
GET https://obsidian.rip/api/v1/applications/{app}/licenses?environment=production&limit=50
Authorization: Bearer <management credential>
Accept: application/jsonConventions
- Every route lives under
/api/v1, takes and returns JSON, and echoes anX-Request-IDyou can quote when asking about a call. - Application routes take
?environment=(development,staging, or the defaultproduction). - Lists answer
{items: […]}. Lists that grow (licenses, devices, sessions, events, customers, resellers, campaigns) also answernext_cursor; pass it back as?cursor=with?limit=up to 500. - Anything that mints (licenses, bundle codes, credits) takes an
Idempotency-Keyheader. A retry with the same key answers the same thing and never reveals a secret twice. - Writes that must not race carry
If-Matchwith the revision you read; a stale write is refused with409 STALE_WRITE. - Requests are metered per credential and per address; a ceiling reached answers
429 RATE_LIMITEDand clears within a minute.
Errors
A refusal is a JSON body with a stable code, a sentence for a person, and the request id.
{ "error": { "code": "FORBIDDEN", "message": "This credential's scopes do not include licenses.write.", "request_id": "…" } }| Status | Meaning |
|---|---|
| 400 | INVALID_INPUT: a field is missing, malformed or not accepted. |
| 401 | No credential, or one that is expired or revoked. |
| 403 | FORBIDDEN or a scope refusal: the credential is valid but this route or object is outside what it holds. |
| 404 | Not found, or outside the caller's view. The two are indistinguishable on purpose. |
| 409 | A conflict: STALE_WRITE, ALREADY_ISSUED, and the like. |
| 429 | RATE_LIMITED. Retry after a minute. |
| 503 | A dependency is unavailable. Safe to retry; nothing was written. |
Reference
Every route the API answers, read from its own OpenAPI document when this page renders. Public routes need no credential. Request and response bodies, with a try-it that calls each route as you, are on the workspace's API page once you sign in.
Applications, licenses, devices, sessions, events
GET/applications | Applications in scope |
POST/applications | Create an application with a Standard product |
GET/applications/{app} | An application and its policy |
PATCH/applications/{app} | Change availability, version floors, device and redemption policy |
DELETE/applications/{app}/{rest} | |
GET/applications/{app}/{rest} | |
PATCH/applications/{app}/{rest} | |
POST/applications/{app}/{rest} | |
GET/applications/{app}/announcements | Announcements to this application's holders |
POST/applications/{app}/announcements | Announce something to this application's holders |
DELETE/applications/{app}/announcements/{announcement} | Withdraw an announcement |
PATCH/applications/{app}/announcements/{announcement} | Change an announcement |
GET/applications/{app}/bundles | Bundles of this application with their contents |
POST/applications/{app}/bundles | Create a bundle |
DELETE/applications/{app}/bundles/{bundle} | Delete a bundle with no outstanding codes or mappings |
PATCH/applications/{app}/bundles/{bundle} | Rename, describe or retire a bundle |
GET/applications/{app}/bundles/{bundle}/codes | A bundle's codes, newest first |
POST/applications/{app}/bundles/{bundle}/codes | Mint bundle codes; shown once here, retained for reveal |
POST/applications/{app}/bundles/{bundle}/codes/{code}/revoke | Revoke an unredeemed bundle code |
POST/applications/{app}/bundles/{bundle}/codes/reveal | Show bundle codes again; each reveal is recorded |
POST/applications/{app}/bundles/{bundle}/issue | Issue a bundle's licenses directly, all at once |
POST/applications/{app}/bundles/{bundle}/products | Add a product with its terms to a bundle |
DELETE/applications/{app}/bundles/{bundle}/products/{entry} | Remove a product from a bundle |
GET/applications/{app}/config | The current configuration of an environment, every visibility, with its revision (ETag) |
POST/applications/{app}/config | Write the whole value map as a new revision; If-Match: <revision> refuses a stale write |
GET/applications/{app}/config/keys | Configuration keys: schema and visibility |
POST/applications/{app}/config/keys | Declare a configuration key with its schema and visibility |
DELETE/applications/{app}/config/keys/{key} | Remove a key; its values stay in past revisions and leave the runtime's view |
PATCH/applications/{app}/config/keys/{key} | Change a key's visibility, schema or description |
GET/applications/{app}/config/revisions | Revision history of an environment's configuration |
GET/applications/{app}/config/revisions/{revision} | One revision, with its values |
POST/applications/{app}/config/rollback | Restore a past revision's values as a new revision |
GET/applications/{app}/content/categories | Content categories of this application's products |
POST/applications/{app}/content/categories | Create a content category under a product |
DELETE/applications/{app}/content/categories/{category} | Delete a category and every item under it |
PATCH/applications/{app}/content/categories/{category} | Change a category's name, schema, review requirement or cap |
GET/applications/{app}/content/items | Content items, newest first, by category or state |
GET/applications/{app}/content/items/{item} | One item with every revision and its moderation record |
POST/applications/{app}/content/items/{item}/restore | Restore a taken-down item; its last approved revision is live again |
POST/applications/{app}/content/items/{item}/revisions/{revision}/approve | Approve a revision under review; it becomes the live one |
POST/applications/{app}/content/items/{item}/revisions/{revision}/reject | Reject a revision under review; the live one stays live |
POST/applications/{app}/content/items/{item}/takedown | Take an item down: hidden from the marketplace and unreadable |
GET/applications/{app}/content/queue | Revisions awaiting review, oldest first |
GET/applications/{app}/denials | Every runtime denial code, the platform's cause, and this application's own words over it |
PATCH/applications/{app}/denials/{code} | Say, in your own words, what a denial code means to your users (null message clears it) |
GET/applications/{app}/devices | Devices in this environment, newest first, cursor-paged |
POST/applications/{app}/devices/{id}/block | Block a device |
POST/applications/{app}/devices/{id}/reset | Free a device slot on the holder's behalf |
POST/applications/{app}/devices/{id}/revoke-key | Revoke a device's enrolled key; the machine must enrol again before it can sign in |
POST/applications/{app}/devices/{id}/unblock | Unblock a device |
GET/applications/{app}/events | Events in this environment, newest first, cursor-paged |
GET/applications/{app}/invites | Product invites, newest first |
POST/applications/{app}/invites | Create product invites; codes shown once here, retained for reveal |
POST/applications/{app}/invites/{invite}/revoke | Revoke an unclaimed invite with a reason |
POST/applications/{app}/invites/reveal | Show invite codes again; each reveal is recorded |
GET/applications/{app}/licenses | Licenses in this environment, cursor-paged |
POST/applications/{app}/licenses | Issue one key or a batch (Idempotency-Key required) |
GET/applications/{app}/licenses/{license} | One license with its holder and product |
PATCH/applications/{app}/licenses/{license} | Change status, cap, expiry, layers or overrides |
POST/applications/{app}/licenses/{license}/extend | Add time to a license; stacks on what is left |
POST/applications/{app}/licenses/{license}/freeze | Stop a license's clock (once per license); ends its sessions |
POST/applications/{app}/licenses/{license}/revoke | Revoke a license and its active sessions atomically, with a reason the SDK can show |
GET/applications/{app}/licenses/{license}/seats | Seat occupancy: the budget, the sessions holding a seat, what is free |
GET/applications/{app}/licenses/{license}/time | The account of a license's time: every change, who, why |
POST/applications/{app}/licenses/{license}/unfreeze | Start the clock again; returns exactly the time held |
POST/applications/{app}/licenses/keys | Show retained keys again; each reveal is recorded |
POST/applications/{app}/licenses/revoke | Revoke a batch and its sessions, all or nothing |
GET/applications/{app}/oauth-clients | Hosted-login clients and the endpoints they use |
POST/applications/{app}/oauth-clients | Register a public client with its redirect allowlist |
PATCH/applications/{app}/oauth-clients/{client} | Change a client; deactivating ends its sessions |
POST/applications/{app}/oauth-clients/{client}/revoke-sessions | End every hosted-login session of this client |
GET/applications/{app}/preference-keys | Preference keys holders may set, with their schema |
POST/applications/{app}/preference-keys | Declare a preference key holders may set |
DELETE/applications/{app}/preference-keys/{key} | Remove a preference key and every holder's value under it |
GET/applications/{app}/products | Products in this environment |
POST/applications/{app}/products | Create a product |
PATCH/applications/{app}/products/{product} | Rename, re-entitle or deactivate a product |
GET/applications/{app}/referral-program | The referral program of this application, with its counts |
POST/applications/{app}/referral-program | Set the referral program: the buyer's discount and the referrer's reward |
GET/applications/{app}/referrals | What each referred order earned, newest first |
GET/applications/{app}/release-policies | Rollout policy per channel |
POST/applications/{app}/release-policies | Set a channel's rollout policy (unpublished until you publish it) |
DELETE/applications/{app}/release-policies/{channel} | Withdraw a channel's rollout policy |
POST/applications/{app}/release-policies/{channel}/publish | Publish a channel's rollout policy: the runtime carries and enforces it from now |
GET/applications/{app}/release-policies/simulate | How many active sessions a required floor would deny, before you publish it |
GET/applications/{app}/releases | Releases with their files and download counts; channels |
POST/applications/{app}/releases | Describe a release on a channel (unpublished) |
DELETE/applications/{app}/releases/{release} | Delete an unpublished release and its files |
GET/applications/{app}/releases/{release} | One release with its files |
PATCH/applications/{app}/releases/{release} | Edit notes or gating; publish or unpublish |
GET/applications/{app}/releases/{release}/downloads | Who was granted which file of this release, from where |
POST/applications/{app}/releases/{release}/files | Declare a file and get a signed upload URL |
DELETE/applications/{app}/releases/{release}/files/{file} | Remove a file from a release |
POST/applications/{app}/releases/{release}/files/{file}/approve | Approve a file's provenance: bind its digest, SDK version and profile; builds need this |
POST/applications/{app}/releases/{release}/files/{file}/complete | Confirm the upload landed with the declared size |
POST/applications/{app}/releases/{release}/files/{file}/grant | A short-lived signed download for staff; recorded like any grant |
POST/applications/{app}/releases/{release}/files/{file}/revoke-approval | Revoke a file's approval; every artifact built from it stops being delivered |
GET/applications/{app}/rewards | Earned rewards of this application with who is earning and who can claim |
POST/applications/{app}/rewards | Define an earned reward: the condition, how long, and the payout |
DELETE/applications/{app}/rewards/{reward} | Delete a reward and every holder's progress under it |
PATCH/applications/{app}/rewards/{reward} | Change a reward's name, threshold, payout or state |
GET/applications/{app}/sessions | Sessions in this environment, newest first, cursor-paged |
POST/applications/{app}/sessions/{id}/terminate | End a runtime session |
GET/applications/{app}/vault | Vault values this application holds, their gates and versions, never their contents |
POST/applications/{app}/vault | Hold a secret for live sessions of this application |
DELETE/applications/{app}/vault/{item} | Delete a vault value; sessions stop receiving it at once |
PATCH/applications/{app}/vault/{item} | Change a vault value's description or entitlement gate |
POST/applications/{app}/vault/{item}/reveal | Read a vault value back in clear, recorded as an event |
POST/applications/{app}/vault/{item}/rotate | Replace a vault value; sessions get the new one when they ask |
Customers
GET/customers | Customers with their license counts, newest first, cursor-paged; ?q= by address |
PATCH/customers/{customer} | Ban or reinstate a customer; a ban ends live sessions |
POST/customers/{customer}/keys | Show a customer's retained keys again; each reveal is recorded |
GET/customers/{customer}/licenses | A customer's licenses across applications |
Resellers
GET/resellers | Resellers in this actor's branch, with balances and operators, cursor-paged |
POST/resellers | Create a reseller with its product grants |
GET/resellers/{reseller} | Reseller detail: policy, balance, pools, subtree |
PATCH/resellers/{reseller} | Suspend or resume a reseller and everything beneath it |
DELETE/resellers/{reseller}/{rest} | |
GET/resellers/{reseller}/{rest} | |
PATCH/resellers/{reseller}/{rest} | |
POST/resellers/{reseller}/{rest} | |
POST/resellers/{reseller}/credits | Add prepaid credits (Idempotency-Key required) |
POST/resellers/{reseller}/operators | Invite an operator; the link is shown once |
DELETE/resellers/{reseller}/operators/{operator} | Remove an operator |
Webhooks
GET/webhooks | Endpoints with their queue and dead-letter counts |
POST/webhooks | Register an HTTPS endpoint; the signing secret is shown once |
GET/webhooks/{webhook} | One endpoint with delivery counts by state |
PATCH/webhooks/{webhook} | Change the destination or the subscriptions |
GET/webhooks/{webhook}/deliveries | Deliveries to an endpoint, cursor-paged, filterable by state |
GET/webhooks/{webhook}/deliveries/{delivery} | One delivery: the event as sent and every attempt |
POST/webhooks/{webhook}/disable | Disable an endpoint |
POST/webhooks/{webhook}/enable | Re-enable; delivery resumes from now, no backfill |
POST/webhooks/{webhook}/replay | Deliver these events to this endpoint again, now |
POST/webhooks/{webhook}/rotate | New signing secret; deliveries carry both signatures until the grace ends |
Events and the feed
GET/events | The organization's event log, cursor-paged and filterable |
GET/events/catalog | The catalog: every event action, denial code and mail kind this deployment knows |
GET/events/feed | Forward event feed in sequence order after an opaque, scope-bound cursor |
Commerce and orders
GET/commerce/connections | Storefront connections, with their webhook URLs |
POST/commerce/connections | Connect a storefront; secrets are write-only |
GET/commerce/connections/{connection} | One connection |
PATCH/commerce/connections/{connection} | Rename, rotate the secret, set the API key, enable or disable |
POST/commerce/connections/{connection}/coupons/sync | Converge the provider's coupons to this organization's; reports every change |
GET/commerce/connections/{connection}/mappings | What each storefront product produces here |
POST/commerce/connections/{connection}/mappings | Map a storefront product to a license product and its terms |
DELETE/commerce/connections/{connection}/mappings/{mapping} | Remove a mapping; orders already fulfilled keep their licenses |
PATCH/commerce/connections/{connection}/mappings/{mapping} | Change a mapping's license terms |
POST/commerce/webhooks/{provider}/{connection} | A storefront's signed order webhook (SellAuth dynamic delivery and notifications; the custom contract) · public |
Orders
GET/orders | Normalised orders across connections, cursor-paged |
GET/orders/{order} | One order: lines, the licenses they produced, what the storefront sent |
POST/orders/{order}/items/{item}/replay | Run a line's fulfilment again; resumes idempotently and re-sends the keys |
POST/orders/{order}/outcome | Record a refund or chargeback by hand and apply each line's rule |
Hosted sign-in
GET/hosted/authorize | Start a hosted sign-in (Authorization Code + PKCE) · public |
POST/hosted/bridge | A single-use, two-minute bridge from a hosted sign-in into a runtime session on one device · public |
GET/hosted/requests/{request} | What a pending sign-in request is for · public |
POST/hosted/revoke | Revoke a token pair · public |
POST/hosted/token | Exchange a code, or rotate a refresh token · public |
GET/hosted/userinfo | The holder and entitlements behind an access token · public |
Brands
DELETE/brands | |
GET/brands | |
PATCH/brands | |
POST/brands | |
DELETE/brands/{rest} | |
GET/brands/{rest} | |
PATCH/brands/{rest} | |
POST/brands/{rest} |
Custom hosts
GET/hosts | Custom hostnames with their verification records and state |
POST/hosts | Register a hostname; answers the DNS records to set |
DELETE/hosts/{host} | Detach a hostname: the edge stops issuing for it and the platform stops answering on it |
PATCH/hosts/{host} | Change which brand a host shows |
POST/hosts/{host}/verify | Check the DNS records now; the host goes live on a pass |
Protected builds
GET/builds | Build jobs, newest first, with the queue's depth |
GET/builds/{build} | One build with its watermark and queue position |
POST/builds/{build}/revoke | Revoke an artifact: no further delivery, no key release |
GET/builds/adapters | The adapter compatibility matrix: what each protection profile does and does not do |
POST/builds/identify | Attribute a delivered artifact by its watermark trailer |
Campaigns
GET/campaigns | Campaigns with their recipient counts by state |
POST/campaigns | Draft a campaign: subject, body, audience |
GET/campaigns/{campaign} | One campaign with its reach (drafts) or its recipients and their states |
POST/campaigns/{campaign}/cancel | Stop a campaign; what has not gone does not go |
POST/campaigns/{campaign}/retry | Re-queue only the recipients that failed |
POST/campaigns/{campaign}/send | Materialise the recipients and start sending; the worker delivers a bounded number per cycle |
POST/campaigns/{campaign}/test | Send the campaign to yourself |
Mail templates
GET/mail/templates | Mail templates: the kinds with their placeholders and defaults, and this organization's own |
POST/mail/templates | Write the template for a kind, organization-wide or for one brand; placeholders are checked |
DELETE/mail/templates/{template} | Remove a template; the platform's default applies again |
POST/mail/templates/preview | Render a template with sample values |
Storefront
GET/storefront | This organization's storefront, its catalog, and the products it could list |
PATCH/storefront | Change the storefront's profile, slug or visibility |
POST/storefront | Create the storefront with its slug and profile |
POST/storefront/products | List a product in the catalog with its price and checkout link |
DELETE/storefront/products/{entry} | Remove a catalog entry |
PATCH/storefront/products/{entry} | Change a catalog entry |
Coupons
GET/coupons | Coupons, with what the provider holds and what orders used them |
POST/coupons | Create a coupon; sync pushes it to the provider |
PATCH/coupons/{coupon} | Change a coupon; sync converges the provider to it |
Referrals
GET/referrals/payouts | Pending cash balances per referrer, and every payout batch |
POST/referrals/payouts | Gather a referrer's pending cash rewards into one batch |
POST/referrals/payouts/{batch}/pay | Mark a batch paid, once; the money moved outside the platform |
POST/referrals/payouts/{batch}/void | Void a pending batch with a reason; it can never be paid |
Rewards
GET/rewards/{app}/stats | Aggregate reward statistics of an application that opted in · public |
Export and import
GET/migrations | Imports staged or run, with their reports |
POST/migrations | Stage an import: normalise, validate and report. This is the dry run; nothing is written |
GET/migrations/{migration} | One import: its report, progress and per-row outcomes |
POST/migrations/{migration}/run | Run pending rows, each recorded; call again until done. A stopped run resumes here |
Linked accounts
GET/accounts | Linked accounts with their latest observation, newest first; `?provider= &flagged=true` |
GET/accounts/{account} | One linked account and its observation history |
GET/accounts/aggregate | The rate over the population: flagged now, newly flagged, over a window |
GET/accounts/providers | Account providers: what the deployment can do, what this organization enabled |
PATCH/accounts/providers/{provider} | Enable a provider for holders, and set how often it is polled and how long observations are kept |
Review queue
GET/review | Flags raised by the review rules, unresolved first; a prompt to look, never an action |
POST/review/acknowledge | Mark an open flag as being looked at, by you |
POST/review/note | Add an attributed note to a flag |
POST/review/resolve | Resolve a flag with what you concluded; the flag is kept |
GET/review/rules | Every review rule with its window and threshold, as code |
Analytics
GET/analytics | Aggregates over real rows with denominator, window and measurement time; null with a reason where unmeasurable |
GET/analytics/daily | Sign-ins, refusals and issuance per day over the window |
Discord
GET/discord | The bot: whether it is available, how to install it, connected guilds |
POST/discord/guilds | Connect a server the bot has been installed in |
DELETE/discord/guilds/{guild} | Forget a server: its rules, grants and the record of what was granted |
PATCH/discord/guilds/{guild} | Pause or resume the bot in a server (roles it granted stay as they are) |
GET/discord/guilds/{guild}/grants | Who may use commands about other members here |
POST/discord/guilds/{guild}/grants | Let a role or a member use a command about other members in this server |
DELETE/discord/guilds/{guild}/grants/{grant} | Withdraw a command grant |
GET/discord/guilds/{guild}/members | Members the bot has seen, whether linked, and the roles it granted them |
GET/discord/guilds/{guild}/rules | What earns which role in this server |
POST/discord/guilds/{guild}/rules | A license or entitlement earns a role |
DELETE/discord/guilds/{guild}/rules/{rule} | Remove a rule; the role is taken back from those who held it through it |
POST/discord/sync | Look at every linked member's roles again; the worker does it in the background |
Management credentials
GET/api-credentials | Credentials, their bindings and rotation state |
POST/api-credentials | Issue a credential; the secret is shown once |
GET/api-credentials/{credential} | One credential |
POST/api-credentials/{credential}/revoke | Revoke a credential; current and previous secrets stop at once |
POST/api-credentials/{credential}/rotate | Mint a new secret; the old one answers until the grace ends |
Team
GET/team | Members, invitations and what this actor may assign |
POST/team/invites | Invite a member; the link is shown once |
POST/team/invites/{invite}/revoke | Revoke an unaccepted invitation |
DELETE/team/members/{member} | Remove a member |
PATCH/team/members/{member} | Change a member's role or page grants |
Settings
GET/settings | Organization identity |
PATCH/settings | Rename the organization |
Licenses across applications
POST/licenses/compensate | Extend every active license in a scope by N hours; applied by the worker in chunks |
GET/licenses/compensations | Compensation batches and their progress |
access-requests
POST/access-requests | Ask for private access from the public website; stored for the platform's operators, acknowledged with nothing else · public |
billing
GET/billing | This organization's plan, usage against its limits, and subscription status |
POST/billing/checkout | Open a Stripe Checkout to move this organization onto a plan |
POST/billing/portal | Open the Stripe billing portal for self-serve card and plan management |
POST/billing/stripe/webhook | Stripe subscription lifecycle events, signature-verified · public |
config
GET/config/{app}/{environment} | An application's PUBLIC configuration values, by application id · public |
explain
GET/explain/{app}/{code} | What a runtime denial code means, in the seller's words · public |
export
GET/export | Export the organization: the manifest and every entity file; keys only when asked, by an Owner |
integrations
GET/integrations | What this deployment can do, from its configuration, and what each missing thing needs |
limits
GET/limits | The caller's enforced limits, read from the counters that enforce them |
me
GET/me | The signed-in member, their organization and CSRF token |
openapi
GET/openapi.json | The generated API reference (OpenAPI 3.1), from the route table; public and sanitised · public |
overview
GET/overview | Headline counts, applications and recent events |
shield
GET/shield/tpm-roots | TPM roots this organization trusts, and whether attestation is possible |
POST/shield/tpm-roots | Trust a TPM vendor root, so an attested key can earn tpm_key |
DELETE/shield/tpm-roots/{root} | Stop trusting a root; devices already admitted keep their class |
status
GET/status/{app} | An application's public status, derived from what gates access (opt-in) · public |
store
GET/store/{slug} | A seller's public storefront by slug · public |
validity
POST/validity | Whether a key would be accepted now: a coarse boolean, rate-limited (opt-in) · public |