Skip to content

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.

A requesthttp
GET https://obsidian.rip/api/v1/applications/{app}/licenses?environment=production&limit=50
Authorization: Bearer <management credential>
Accept: application/json

Conventions

  • Every route lives under /api/v1, takes and returns JSON, and echoes an X-Request-ID you can quote when asking about a call.
  • Application routes take ?environment= (development, staging, or the default production).
  • Lists answer {items: […]}. Lists that grow (licenses, devices, sessions, events, customers, resellers, campaigns) also answer next_cursor; pass it back as ?cursor= with ?limit= up to 500.
  • Anything that mints (licenses, bundle codes, credits) takes an Idempotency-Key header. A retry with the same key answers the same thing and never reveals a secret twice.
  • Writes that must not race carry If-Match with the revision you read; a stale write is refused with 409 STALE_WRITE.
  • Requests are metered per credential and per address; a ceiling reached answers 429 RATE_LIMITED and clears within a minute.

Errors

A refusal is a JSON body with a stable code, a sentence for a person, and the request id.

A refusaljson
{ "error": { "code": "FORBIDDEN", "message": "This credential's scopes do not include licenses.write.", "request_id": "…" } }
StatusMeaning
400INVALID_INPUT: a field is missing, malformed or not accepted.
401No credential, or one that is expired or revoked.
403FORBIDDEN or a scope refusal: the credential is valid but this route or object is outside what it holds.
404Not found, or outside the caller's view. The two are indistinguishable on purpose.
409A conflict: STALE_WRITE, ALREADY_ISSUED, and the like.
429RATE_LIMITED. Retry after a minute.
503A 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/applicationsApplications in scope
POST/applicationsCreate 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}/announcementsAnnouncements to this application's holders
POST/applications/{app}/announcementsAnnounce 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}/bundlesBundles of this application with their contents
POST/applications/{app}/bundlesCreate 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}/codesA bundle's codes, newest first
POST/applications/{app}/bundles/{bundle}/codesMint bundle codes; shown once here, retained for reveal
POST/applications/{app}/bundles/{bundle}/codes/{code}/revokeRevoke an unredeemed bundle code
POST/applications/{app}/bundles/{bundle}/codes/revealShow bundle codes again; each reveal is recorded
POST/applications/{app}/bundles/{bundle}/issueIssue a bundle's licenses directly, all at once
POST/applications/{app}/bundles/{bundle}/productsAdd a product with its terms to a bundle
DELETE/applications/{app}/bundles/{bundle}/products/{entry}Remove a product from a bundle
GET/applications/{app}/configThe current configuration of an environment, every visibility, with its revision (ETag)
POST/applications/{app}/configWrite the whole value map as a new revision; If-Match: <revision> refuses a stale write
GET/applications/{app}/config/keysConfiguration keys: schema and visibility
POST/applications/{app}/config/keysDeclare 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/revisionsRevision history of an environment's configuration
GET/applications/{app}/config/revisions/{revision}One revision, with its values
POST/applications/{app}/config/rollbackRestore a past revision's values as a new revision
GET/applications/{app}/content/categoriesContent categories of this application's products
POST/applications/{app}/content/categoriesCreate 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/itemsContent 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}/restoreRestore a taken-down item; its last approved revision is live again
POST/applications/{app}/content/items/{item}/revisions/{revision}/approveApprove a revision under review; it becomes the live one
POST/applications/{app}/content/items/{item}/revisions/{revision}/rejectReject a revision under review; the live one stays live
POST/applications/{app}/content/items/{item}/takedownTake an item down: hidden from the marketplace and unreadable
GET/applications/{app}/content/queueRevisions awaiting review, oldest first
GET/applications/{app}/denialsEvery 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}/devicesDevices in this environment, newest first, cursor-paged
POST/applications/{app}/devices/{id}/blockBlock a device
POST/applications/{app}/devices/{id}/resetFree a device slot on the holder's behalf
POST/applications/{app}/devices/{id}/revoke-keyRevoke a device's enrolled key; the machine must enrol again before it can sign in
POST/applications/{app}/devices/{id}/unblockUnblock a device
GET/applications/{app}/eventsEvents in this environment, newest first, cursor-paged
GET/applications/{app}/invitesProduct invites, newest first
POST/applications/{app}/invitesCreate product invites; codes shown once here, retained for reveal
POST/applications/{app}/invites/{invite}/revokeRevoke an unclaimed invite with a reason
POST/applications/{app}/invites/revealShow invite codes again; each reveal is recorded
GET/applications/{app}/licensesLicenses in this environment, cursor-paged
POST/applications/{app}/licensesIssue 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}/extendAdd time to a license; stacks on what is left
POST/applications/{app}/licenses/{license}/freezeStop a license's clock (once per license); ends its sessions
POST/applications/{app}/licenses/{license}/revokeRevoke a license and its active sessions atomically, with a reason the SDK can show
GET/applications/{app}/licenses/{license}/seatsSeat occupancy: the budget, the sessions holding a seat, what is free
GET/applications/{app}/licenses/{license}/timeThe account of a license's time: every change, who, why
POST/applications/{app}/licenses/{license}/unfreezeStart the clock again; returns exactly the time held
POST/applications/{app}/licenses/keysShow retained keys again; each reveal is recorded
POST/applications/{app}/licenses/revokeRevoke a batch and its sessions, all or nothing
GET/applications/{app}/oauth-clientsHosted-login clients and the endpoints they use
POST/applications/{app}/oauth-clientsRegister 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-sessionsEnd every hosted-login session of this client
GET/applications/{app}/preference-keysPreference keys holders may set, with their schema
POST/applications/{app}/preference-keysDeclare 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}/productsProducts in this environment
POST/applications/{app}/productsCreate a product
PATCH/applications/{app}/products/{product}Rename, re-entitle or deactivate a product
GET/applications/{app}/referral-programThe referral program of this application, with its counts
POST/applications/{app}/referral-programSet the referral program: the buyer's discount and the referrer's reward
GET/applications/{app}/referralsWhat each referred order earned, newest first
GET/applications/{app}/release-policiesRollout policy per channel
POST/applications/{app}/release-policiesSet 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}/publishPublish a channel's rollout policy: the runtime carries and enforces it from now
GET/applications/{app}/release-policies/simulateHow many active sessions a required floor would deny, before you publish it
GET/applications/{app}/releasesReleases with their files and download counts; channels
POST/applications/{app}/releasesDescribe 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}/downloadsWho was granted which file of this release, from where
POST/applications/{app}/releases/{release}/filesDeclare 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}/approveApprove a file's provenance: bind its digest, SDK version and profile; builds need this
POST/applications/{app}/releases/{release}/files/{file}/completeConfirm the upload landed with the declared size
POST/applications/{app}/releases/{release}/files/{file}/grantA short-lived signed download for staff; recorded like any grant
POST/applications/{app}/releases/{release}/files/{file}/revoke-approvalRevoke a file's approval; every artifact built from it stops being delivered
GET/applications/{app}/rewardsEarned rewards of this application with who is earning and who can claim
POST/applications/{app}/rewardsDefine 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}/sessionsSessions in this environment, newest first, cursor-paged
POST/applications/{app}/sessions/{id}/terminateEnd a runtime session
GET/applications/{app}/vaultVault values this application holds, their gates and versions, never their contents
POST/applications/{app}/vaultHold 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}/revealRead a vault value back in clear, recorded as an event
POST/applications/{app}/vault/{item}/rotateReplace a vault value; sessions get the new one when they ask

Customers

GET/customersCustomers 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}/keysShow a customer's retained keys again; each reveal is recorded
GET/customers/{customer}/licensesA customer's licenses across applications

Resellers

GET/resellersResellers in this actor's branch, with balances and operators, cursor-paged
POST/resellersCreate 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}/creditsAdd prepaid credits (Idempotency-Key required)
POST/resellers/{reseller}/operatorsInvite an operator; the link is shown once
DELETE/resellers/{reseller}/operators/{operator}Remove an operator

Webhooks

GET/webhooksEndpoints with their queue and dead-letter counts
POST/webhooksRegister 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}/deliveriesDeliveries 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}/disableDisable an endpoint
POST/webhooks/{webhook}/enableRe-enable; delivery resumes from now, no backfill
POST/webhooks/{webhook}/replayDeliver these events to this endpoint again, now
POST/webhooks/{webhook}/rotateNew signing secret; deliveries carry both signatures until the grace ends

Events and the feed

GET/eventsThe organization's event log, cursor-paged and filterable
GET/events/catalogThe catalog: every event action, denial code and mail kind this deployment knows
GET/events/feedForward event feed in sequence order after an opaque, scope-bound cursor

Commerce and orders

GET/commerce/connectionsStorefront connections, with their webhook URLs
POST/commerce/connectionsConnect 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/syncConverge the provider's coupons to this organization's; reports every change
GET/commerce/connections/{connection}/mappingsWhat each storefront product produces here
POST/commerce/connections/{connection}/mappingsMap 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/ordersNormalised orders across connections, cursor-paged
GET/orders/{order}One order: lines, the licenses they produced, what the storefront sent
POST/orders/{order}/items/{item}/replayRun a line's fulfilment again; resumes idempotently and re-sends the keys
POST/orders/{order}/outcomeRecord a refund or chargeback by hand and apply each line's rule

Hosted sign-in

GET/hosted/authorizeStart a hosted sign-in (Authorization Code + PKCE) · public
POST/hosted/bridgeA 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/revokeRevoke a token pair · public
POST/hosted/tokenExchange a code, or rotate a refresh token · public
GET/hosted/userinfoThe 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/hostsCustom hostnames with their verification records and state
POST/hostsRegister 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}/verifyCheck the DNS records now; the host goes live on a pass

Protected builds

GET/buildsBuild jobs, newest first, with the queue's depth
GET/builds/{build}One build with its watermark and queue position
POST/builds/{build}/revokeRevoke an artifact: no further delivery, no key release
GET/builds/adaptersThe adapter compatibility matrix: what each protection profile does and does not do
POST/builds/identifyAttribute a delivered artifact by its watermark trailer

Campaigns

GET/campaignsCampaigns with their recipient counts by state
POST/campaignsDraft a campaign: subject, body, audience
GET/campaigns/{campaign}One campaign with its reach (drafts) or its recipients and their states
POST/campaigns/{campaign}/cancelStop a campaign; what has not gone does not go
POST/campaigns/{campaign}/retryRe-queue only the recipients that failed
POST/campaigns/{campaign}/sendMaterialise the recipients and start sending; the worker delivers a bounded number per cycle
POST/campaigns/{campaign}/testSend the campaign to yourself

Mail templates

GET/mail/templatesMail templates: the kinds with their placeholders and defaults, and this organization's own
POST/mail/templatesWrite 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/previewRender a template with sample values

Storefront

GET/storefrontThis organization's storefront, its catalog, and the products it could list
PATCH/storefrontChange the storefront's profile, slug or visibility
POST/storefrontCreate the storefront with its slug and profile
POST/storefront/productsList 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/couponsCoupons, with what the provider holds and what orders used them
POST/couponsCreate a coupon; sync pushes it to the provider
PATCH/coupons/{coupon}Change a coupon; sync converges the provider to it

Referrals

GET/referrals/payoutsPending cash balances per referrer, and every payout batch
POST/referrals/payoutsGather a referrer's pending cash rewards into one batch
POST/referrals/payouts/{batch}/payMark a batch paid, once; the money moved outside the platform
POST/referrals/payouts/{batch}/voidVoid a pending batch with a reason; it can never be paid

Rewards

GET/rewards/{app}/statsAggregate reward statistics of an application that opted in · public

Export and import

GET/migrationsImports staged or run, with their reports
POST/migrationsStage 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}/runRun pending rows, each recorded; call again until done. A stopped run resumes here

Linked accounts

GET/accountsLinked accounts with their latest observation, newest first; `?provider= &flagged=true`
GET/accounts/{account}One linked account and its observation history
GET/accounts/aggregateThe rate over the population: flagged now, newly flagged, over a window
GET/accounts/providersAccount 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/reviewFlags raised by the review rules, unresolved first; a prompt to look, never an action
POST/review/acknowledgeMark an open flag as being looked at, by you
POST/review/noteAdd an attributed note to a flag
POST/review/resolveResolve a flag with what you concluded; the flag is kept
GET/review/rulesEvery review rule with its window and threshold, as code

Analytics

GET/analyticsAggregates over real rows with denominator, window and measurement time; null with a reason where unmeasurable
GET/analytics/dailySign-ins, refusals and issuance per day over the window

Discord

GET/discordThe bot: whether it is available, how to install it, connected guilds
POST/discord/guildsConnect 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}/grantsWho may use commands about other members here
POST/discord/guilds/{guild}/grantsLet 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}/membersMembers the bot has seen, whether linked, and the roles it granted them
GET/discord/guilds/{guild}/rulesWhat earns which role in this server
POST/discord/guilds/{guild}/rulesA 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/syncLook at every linked member's roles again; the worker does it in the background

Management credentials

GET/api-credentialsCredentials, their bindings and rotation state
POST/api-credentialsIssue a credential; the secret is shown once
GET/api-credentials/{credential}One credential
POST/api-credentials/{credential}/revokeRevoke a credential; current and previous secrets stop at once
POST/api-credentials/{credential}/rotateMint a new secret; the old one answers until the grace ends

Team

GET/teamMembers, invitations and what this actor may assign
POST/team/invitesInvite a member; the link is shown once
POST/team/invites/{invite}/revokeRevoke an unaccepted invitation
DELETE/team/members/{member}Remove a member
PATCH/team/members/{member}Change a member's role or page grants

Settings

GET/settingsOrganization identity
PATCH/settingsRename the organization

Licenses across applications

POST/licenses/compensateExtend every active license in a scope by N hours; applied by the worker in chunks
GET/licenses/compensationsCompensation batches and their progress

access-requests

POST/access-requestsAsk for private access from the public website; stored for the platform's operators, acknowledged with nothing else · public

billing

GET/billingThis organization's plan, usage against its limits, and subscription status
POST/billing/checkoutOpen a Stripe Checkout to move this organization onto a plan
POST/billing/portalOpen the Stripe billing portal for self-serve card and plan management
POST/billing/stripe/webhookStripe 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/exportExport the organization: the manifest and every entity file; keys only when asked, by an Owner

integrations

GET/integrationsWhat this deployment can do, from its configuration, and what each missing thing needs

limits

GET/limitsThe caller's enforced limits, read from the counters that enforce them

me

GET/meThe signed-in member, their organization and CSRF token

openapi

GET/openapi.jsonThe generated API reference (OpenAPI 3.1), from the route table; public and sanitised · public

overview

GET/overviewHeadline counts, applications and recent events

shield

GET/shield/tpm-rootsTPM roots this organization trusts, and whether attestation is possible
POST/shield/tpm-rootsTrust 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/validityWhether a key would be accepted now: a coarse boolean, rate-limited (opt-in) · public