Platform API — the /v1 surface
The tenant-keyed HTTP API a third-party app builds on. JSON in, JSON out, over HTTPS. This page is the map; the machine-readable spec is canonical — point tooling at it:
- Production spec (only ops callable today):
https://api.witbitz.chat/v1/openapi.json(.yaml) - Full design spec (every op,
x-status-labelled):https://api.witbitz.chat/v1/openapi.full.json - Discovery index:
https://api.witbitz.chat/v1/
The Bridge is a separate contract (party signatures, not tenant keys) — see the Bridge.
Base + envelope
- Base URL:
https://api.witbitz.chat/v1 - Success is enveloped
{ "data": <payload>, "meta"?: {...} }; errors are{ "error": { "code", "message", "request_id"? } }. One exception:POST /space(x-envelope: false) returns a raw payload and a flat error shape — unifying it is a tracked follow-up. - Version
1.0.0-beta. Private beta — the contract may change (coordinated with invited integrators) until GA.
Authentication
Two tenant API keys, resolved by the /v1 authorizer to a tenantId:
- Secret key
wsk_— full scope, server-side only. - Publishable key
wpk_— browser-safe, public reads + funnel-entry writes. It identifies a tenant; it does not authenticate an end user. - Per-collection capability tokens (
x-collection-token): the owneradmin_tokenor the add-onlycontributor_token, for owner/contributor actions on a collection. Wallet/license credentials travel in the request body, never a URL.
Capability tokens + raw keys are shown once at creation and stored only as a hash (or KMS-sealed). Store them server-side; lost = re-create.
Operational contract
- Idempotency: money/billable ops (
POST /sessions,/wallets/grant,/collections/{id}/paint) require anIdempotency-Key; same key + same body replays the first 2xx byte-for-byte. - Rate limiting + kill switch: throttled at the gateway; per-tenant/per-key-type limits at the authorizer; an
operator can suspend a tenant (
POST /tenants/{tenant_id}/status) — every key is denied within ~15s. That op andPOST /tenantsare operator-only (X-Admin-Token, not a tenant key) and are deliberately absent from the published spec, so don't go looking for them inopenapi.json. - Secret handling: one-time-secret responses are
Cache-Control: no-store; sensitive fieldsx-sensitive; BYOK provider keys + the webhook secret are KMS-sealed at rest.
The surface (by area)
Agents
- GET /catalog — the public agent catalog (publishable).
Collections — the generic content primitive (opaque metadata + items + curation + a keyless render page)
- POST /collections · GET /collections/{id} · DELETE /collections/{id}
- GET /collections/{id}/render — keyless share page for published content (public)
- items: GET/POST /collections/{id}/items · PATCH/DELETE /collections/{id}/items/{itemId}
- POST /collections/{id}/state — merge opaque app state onto the collection
Artifacts — produced media on a collection
- GET/POST /collections/{id}/artifacts · GET/PATCH/DELETE .../artifacts/{artifactId}
- POST /collections/{id}/paint · POST /collections/{id}/produce
- POST /collections/{id}/artifacts/upload → presigned S3 PUT, then
POST /collections/{id}/artifacts/{artifactId}/ready to stamp it complete — both halves are required
Sessions
- POST /sessions — launch (or re-summon) an agent; metered, idempotent. Accepts either key: the spend
credential is the coupon in the body, so a publishable key launches nothing without a funded wallet.
Credits
- POST /wallets/lookup · POST /wallets/grant · POST /checkout · GET /usage
- POST /wallets/redeem — two presentations: a Lemon Squeezy license_key, or a blinded issuer
{token, sig, epoch}. Both mint a wallet_code shown once. For a token the token IS the idempotency
key — a retry gets 409 already_spent, never a second wallet, so don't send an Idempotency-Key.
- POST /epochs — issuer role required. Publish a batch's PUBLIC key and per-token value, after which
tokens signed by the matching private key are redeemable. The platform holds only public keys, so it can
verify what an issuer signed and can never mint on its behalf. Set once: rotation is a new epoch.
Roles. A tenant is an app (builds a product, spends credit) or an issuer (holds the customer and
the payment relationship, funds wallets from its own prefunded balance) — or both. grant and /epochs
are issuer-only. A tenant with no roles recorded predates the split and is treated as holding both.
Roles are set by an operator, not self-service.
Tenants (onboarding + key management)
- POST /tenants/signup · POST /tenants · POST /tenants/{tenant_id}/status
- GET /tenant · GET/POST /tenant/keys · DELETE /tenant/keys/{keyId}
- PUT /tenant/provider-keys (BYOK, KMS-sealed) · PUT /tenant/webhooks
Spaces — POST /space, the async Space's single op-dispatched endpoint. It is in /v1 but keyless: it
authenticates by key possession, not a tenant key (see the note below).
Infra — GET /turn (ephemeral TURN credentials).
Not at
/v1: ratings,/agents/*,/earningsand/sessions/{id}arex-status: legacy— they exist on the older surface but are not routed here, and calling them returns 404. They appear inopenapi.full.jsonlabelled as such, never in the production spec.
Access
Invite-only during private beta. Email hello@witbitz.chat for keys. Everything above is served openly as a contract; the keys are what's gated.
Notes
- The async Spaces runtime (
/space) is a separate, content-blind surface (key possession, not tenant keys) — see async Spaces and the Space link is the auth. - This page is a summary; the OpenAPI at
/v1/openapi.jsonis the source of truth (schemas, per-op auth, examples).
Machine-readable source: api-reference.md · every doc in one fetch: llms-full.txt (HTML) · ← private-lanes · trust-model →