API reference
The TellWang control-plane API manages Woks and resources; each Wok also exposes its own data API (PostgREST over your tables, auth, storage). Everything Wang and the dashboard do is available here, so automation and CI use the exact same surface.
Base URL & versioning
The control-plane API is versioned in the path; breaking changes ship under a new version, never in place.
https://api.tellwang.com/v1 # control plane (Woks, resources)
https://<wok-id>.tellwang.com/rest/v1 # a Wok's data APIAuthentication
Authenticate with a bearer token: an API key or service-account token for the control plane; the Wok's anon or service-role key for its data API. Every request is authorized server-side against the caller's org/project scope.
curl https://api.tellwang.com/v1/woks \
-H "Authorization: Bearer <api-key>"Conventions
- Idempotency: send an
Idempotency-Keyheader on writes; retries are safe and never double-apply (or double-charge). - Errors: a stable envelope —
{ "error": { "code", "message", "hint", "correlation_id" } }.codeis the machine-readable identifier (classify on this);messageis human-prose;hintis the remediation (sometimes omitted on terse errors);correlation_idis always present — include it when filing a ticket. See Error codes for the 25 most-hit codes with per-code remediation. - Pagination: cursor-based via
?cursor=+limit; responses includenext_cursor. - Rate limits: per-token;
429withRetry-After.
Example — provision a Wok
curl -X POST https://api.tellwang.com/v1/woks \
-H "Authorization: Bearer <api-key>" \
-H "Idempotency-Key: 7f3a-..." \
-d '{ "name": "acme-pos" }'
# → 201 { "id": "wok_abc123", "url": "https://wok_abc123.tellwang.com",
# "anon_key": "...", "state": "ready" }Diagnostics
Three layers, from cheapest to most detailed. All return JSON.
GET /v1/healthz— liveness only.200 {"status":"ok"}when the cp process is running and able to serve. Doesn't say anything about the underlying systems. Use this for load-balancer health checks and CI smoke tests; sub-millisecond.GET /v1/diag— platform semantic health. Walks every subsystem (docker daemon, Caddy, waitlist DB, audit-log chain, image cache, etc.) and reports per-check status + evidence:
Use this when you want to know "is anything wrong, and what is it." Open (no auth) so the platform's status is queryable from outside. Top-level{ "status": "ok", "correlation_id": "4aaebbc7a83773d9", "checks": [ {"name":"swarm", "status":"ok", "detail":"docker daemon reachable"}, {"name":"caddy", "status":"ok", "detail":"running + Caddyfile valid"}, {"name":"cp_db", "status":"ok", "detail":"pgxpool acquire 1.2ms"}, {"name":"audit_log", "status":"ok", "detail":"hash chain intact at id=42117"}, ... ] }statusis the aggregate acrosschecks(ok / degraded / down).GET /v1/diag/wok/{id}— per-wok semantic health. Same shape, but checks scoped to one wok's stack: every container's running-state, postgres readiness, port reachability, replication slot health. Bearer-of-owning-org (or operator-bootstrap); cross-org returns 404 with no leak. Use this in your customer's own dashboards or to debug "my wok stopped working."
The semantic distinction: /healthz answers "should the load balancer keep sending us traffic"; /diag answers "what's right and what's broken, with evidence." The ops-agent's Phase-8 self-heal loop reads /diag as its primary signal.
Wok lifecycle
Provision returns the canonical row; everything below operates against an existing wok. Bearer-of-owning-org on all of them; cross-org returns 404.
GET /v1/woks/{id}— single row:{id, host, url, status, created_at, updated_at}. Useful for "is this wok still mine + alive."GET /v1/woks— list active woks for the calling org.GET /v1/woks/{id}/connection— the wiring sheet. Returns everything@supabase/supabase-jsneeds:
Every wok is reachable at{ "wok_id": "acme-pos", "status": "ready", "url": "https://acme-pos.tellwang.com", // base URL for supabase-js "rest_url": "https://acme-pos.tellwang.com/rest/v1", "anon_key": "eyJhbGciOi...", // client-side JWT, RLS-bound "service_role_key": "eyJhbGciOi...", // SERVER-SIDE ONLY; bypasses RLS "functions_url": "https://acme-pos.tellwang.com/functions/v1", "realtime_url": "https://acme-pos.tellwang.com/realtime/v1", "storage_url": "https://acme-pos.tellwang.com/storage/v1", "auth_url": "https://acme-pos.tellwang.com/auth/v1", "redis_url": "redis://default:<dbpass>@redis:6379" // internal-only, edge fn use }https://<wok-id>.tellwang.comas the supabase-js base URL, with the 5 standard Supabase API path prefixes (/rest/v1,/auth/v1,/realtime/v1,/storage/v1,/functions/v1) routing transparently to the wok's services. Caddy fetches a Let's Encrypt cert on the first HTTPS hit via on-demand TLS (no pre-provisioning step). Keys are minted on-the-fly from the wok's per-instance JWT secret; the secret never leaves the cp.service_role_keybypasses RLS and must never reach a browser — wire it only into server-side processes (edge functions, your backend, CI).redis_urlis internal-only (no external access); edge functions read it asprocess.env.REDIS_URL, see Functions → Redis + BullMQ.POST /v1/woks/{id}/restart— graceful restart of every container in the wok's stack (postgres → gotrue → postgrest → realtime → storage → functions). Picks up env-var changes that PUT/POST/envwrote since the last restart. Idempotent (returns the same shape regardless of pre-state).POST /v1/woks/{id}/resume— re-attach asuspendedwok (free-tier inactivity hibernation). 200 if alreadyready; 409 if already destroyed.DELETE /v1/woks/{id}— tear down the stack + mark destroyed. Postgres volume is retained for 7 days for accidental-deletion recovery; after that the disk is reclaimed.
Audit log
Every state-changing call to the cp writes a row to a tamper-evident, hash-chained audit log. Customers query their own org's chain:
GET /v1/audit-log— list entries for the calling org, newest-first, cursor-paginated. Query params:limit— 1..200 (default 100).cursor— opaque cursor returned asnext_cursorfrom the previous page.action— exact match on action string, e.g.wok.exec_sql,wok.env.set,wok.frontend.deploy.actor_kind— one ofoperator|bearer|internal|system.target_kind— e.g.wok,llm_key,email_domain.since/until— RFC3339 timestamps.
Response shape: {entries:[{id, actor_kind, actor_id, action, target_kind, target_id, http_status, correlation_id, payload, created_at}], next_cursor}. Payload values that touch secrets (env values, key material) are stored as fingerprints — names + hashes only, never plaintext.
The dashboard's Activity card (under YOUR WOKS) hits this endpoint with default filters. For programmatic access, the same Bearer + same shape works from any client.
Wok internals — SQL, functions, realtime, scheduler, env
Once a Wok is provisioned, drive its full surface from the same Bearer-of-owning-org auth. Every endpoint is authorized server-side against the owning org — cross-org access returns 404 with no leak. All available from the dashboard's per-wok widgets too.
SQL console
POST /v1/woks/{id}/exec_sql— body{"sql":"<your-sql>", "no_pgrst_reload":false}. Runs in your wok's db container viapsql; returns{stdout, stderr, exit_code}. Auto-appendsNOTIFY pgrst, 'reload schema'on success so PostgREST picks up new tables immediately (opt-out viano_pgrst_reload). Body cap 1 MiB; stdout/stderr capped at 1 MiB.
Edge functions
POST /v1/woks/{id}/functions/{name}— body{"code":"<deno-typescript>"}. Name^[a-z][a-z0-9-]{0,30}$. Returns 201 with the row +invoke_urlyou can curl immediately. 503CP_FUNCTIONS_NOT_ENABLEDif the Wok predates Functions support; the reconciler heals it on the next sweep.GET /v1/woks/{id}/functions— lists each fn as{name, code_bytes, code_sha256, invoke_url, created_at, updated_at}(omits code body).GET /v1/woks/{id}/functions/{name}— returns the full code body so you can pull what you deployed.DELETE /v1/woks/{id}/functions/{name}— idempotent; missing returns 204.POST /v1/woks/{id}/triggers· body{table, event, function_name}— wires a DB trigger thatpg_notifys on INSERT/UPDATE/DELETE and dispatches to your function.
Functions receive the following env vars at runtime: SUPABASE_URL (the wok's PostgREST), SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, SUPABASE_DB_URL, REDIS_URL (per-wok Redis 7 container, AUTH via the wok's db password). The Redis instance is bullmq-ready out of the box — see Functions → Redis + BullMQ for the canonical pattern.
Realtime publication
PUT /v1/woks/{id}/realtime/tables/{schema}/{name}— adds the table to thesupabase_realtimepublication. Idempotent.GET /v1/woks/{id}/realtime/tables— returns{tables:[{schema, table}], publication}.DELETE /v1/woks/{id}/realtime/tables/{schema}/{name}— drops the table from the publication.GET /v1/woks/{id}/realtime/status— reports wal_level + slot health.
Scheduler (pg_cron)
POST /v1/woks/{id}/scheduler/jobs— body{name, schedule, command}. Schedule = cron expression (0 3 * * *) or interval (30 seconds). Command = any SQL, runs inside your wok's db. Returns 201 with the cron job_id.GET /v1/woks/{id}/scheduler/jobs— lists each as{job_id, name, schedule, command, active}.DELETE /v1/woks/{id}/scheduler/jobs/{name}— unschedule. Idempotent.
Environment variables
PUT /v1/woks/{id}/env/{name}— body{value}. Name^[A-Z][A-Z0-9_]{0,63}$. Encrypted at rest under the cp's KEK (AES-256-GCM envelope). 204 on success. Wired onto the wok's GoTrue + Functions containers; cp restarts them on next mutation.POST /v1/woks/{id}/env— bulk import. Body is one of{"vars":{"NAME1":"v1","NAME2":"v2"}}or{"dotenv":"NAME1=v1\nNAME2=v2"}. Up to 200 names per call; all names validated against the regex BEFORE any write (atomic — one bad name fails the whole batch with the offender list in the error envelope). Single pgx transaction so a network failure mid-batch never leaves a half-applied state. Dotenv parser tolerates# comments,export NAME=valshell-prefix, and matching surrounding single/double quotes. Does NOT auto-restart the wok stack — POST/restartafterward if you need the new values live immediately (the per-var PUT path auto-restarts, the bulk path doesn't because a 20-var batch otherwise burns ~30s on container churn).GET /v1/woks/{id}/env— returns{vars:[{name, created_at, updated_at}]}. Values are NEVER returned (write-only).DELETE /v1/woks/{id}/env/{name}— unset. Idempotent.
Most common use: wire OAuth providers (Google, GitHub, Apple, Azure, Bitbucket, Discord, Facebook, LinkedIn, Notion, Slack, Spotify, Twitch, Twitter, Zoom) by setting GOTRUE_EXTERNAL_<PROVIDER>_ENABLED=true + _CLIENT_ID + _SECRET + _REDIRECT_URI. See Auth → OAuth providers.
curl -X POST https://tellwang.com/v1/woks/$WOK/env \
-H "Authorization: Bearer $TELLWANG_KEY" \
-H "Content-Type: application/json" \
-d '{"dotenv":"GOTRUE_EXTERNAL_GOOGLE_ENABLED=true\nGOTRUE_EXTERNAL_GOOGLE_CLIENT_ID=...\nGOTRUE_EXTERNAL_GOOGLE_SECRET=...\nGOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI=https://..."}'
curl -X POST https://tellwang.com/v1/woks/$WOK/restart \
-H "Authorization: Bearer $TELLWANG_KEY"Account, plans & model gateway
The control plane runs the customer onramp, plan tiers, and a metered model gateway — all under the same Bearer key you provision Woks with.
POST /v1/signup·POST /v1/login— self-serve account + org + first API key from an email and password. Rate-limited. Open by default; where the deployment is invite-gated, signup also takes aninvite_code.GET /v1/plans— the catalog (free / pro / scale). Open (no auth). Response:{plans:[{plan, wok_quota, llm_tokens_grant, llm_requests_grant, price_cents_month}]}. Free plan returnsprice_cents_month: 0; the others quote in USD cents.GET /v1/orgs/{slug}/plan— your plan, its entitlements, and Woks used against the limit.POST /v1/llm-keys— mint a model-gateway key; the grant defaults to (and is capped at) your plan.GETlists balances,/topuprefills toward the plan ceiling,/checkoutbuys beyond it,DELETErevokes. Point any OpenAI-compatible client at/v1/llm/v1/chat/completions. Passstream: truefor SSE streaming responses (see Streaming) — token metering happens after the stream closes, off the finalusagechunk.GET /v1/llm-keys/{prefix}/billing— the Stripe-credit ledger for a key, to reconcile a purchase against the meter.POST /v1/orgs/{slug}/email/domain— attach a sending domain (e.g.mail.acme.com). Returns the DKIM / SPF / MX records to publish, or auto-publishes them when TellWang manages the apex zone.GET /v1/orgs/{slug}/email/domainspolls live status;DELETErevokes. See Email → Bring your own domain.POST /v1/orgs/{slug}/billing/checkout/subscription— buy aproorscalesubscription via Stripe Checkout. Body{plan, success_url, cancel_url}returns a Stripe-hosted checkout URL. On payment, Stripe'scustomer.subscription.createdwebhook flips your org plan +wok_quota; cancellation drops back tofree.GET /v1/orgs/{slug}/billing/subscriptionreads the current status.
Plan changes are self-serve via the subscription checkout above (the operator-only PUT /v1/orgs/{slug}/plan still exists for overrides); per-org quota overrides remain operator-gated.
Prepaid credits
Top up once, debit silently per action. One USD-cents balance per org covers domain registrations today and any future per-call charge. Two top-up rails — Stripe Checkout (card) or Solana USDC (crypto). After the initial deposit there's no second click per buy.
GET /v1/orgs/{slug}/credits— current balance, recent ledger entries, both rails' status.POST /v1/orgs/{slug}/credits/topup— body{amount_usd, success_url?, cancel_url?}. Returns a Stripe Checkout URL; on payment the balance lands within ~5s via the same webhook plumbing the LLM top-up uses. Per-session cap $5000.GET /v1/orgs/{slug}/credits/crypto-deposit— returns the operator's Solana USDC token account address + the per-org memo string (tw:org:<slug>). Send any USDC amount with the memo attached; the watcher credits ~15-45s after confirmation. Idempotent on tx signature.
On any paid action with an insufficient balance, the response is 402 {status:"insufficient_credits", balance_cents, deficit_cents, top_up_url} — the top_up_url is a pre-built Stripe Checkout covering exactly the deficit, so the agent or dashboard hands the customer one click to unblock the buy.
Domain registration
Buy a domain through the Cloudflare Registrar with TellWang as the trustee. Dynamic pricing: Cloudflare's at-cost × 1.30 with a $5 USD floor; pre-flight failures (invalid syntax, unsupported TLD, name unavailable, cap exceeded) are FREE — no Stripe call until the buy succeeds. Supported TLDs cover the standard set (.com .net .org .dev .app .xyz .info .biz .pro .site .online .store .tech .blog .page .click .link .live .world) and a premium tier (.io .ai .co .me .tv .gg) when the CF Registrar API supports them.
POST /v1/orgs/{slug}/domains/check— body{names:[], expected_max_price?}. Returns a per-name quote:{ok, name, tier, at_cost_usd, billed_usd, reason?}. Always check before buy so you know what you'll be charged. Max 20 names per call.POST /v1/orgs/{slug}/domains/buy— body{name, expected_max_price?, auto_renew?, privacy?, registrant_contact?}. Silent debit from the org's prepaid credit balance, then registers via CF, then upserts the row. Returns{status:"active"|"pending"|"failed", domain_row, debit_cents, balance_remaining}. On insufficient credits returns 402 withtop_up_url(see above). On CF-register failure post-debit, the credit is auto-refunded.POST /v1/orgs/{slug}/domains/attach— body{domain}. Attach a domain already in the operator's CF account (no registrar round-trip) so the org can manage its DNS without a fresh registration.GET /v1/orgs/{slug}/domains— list owned domains.GET /v1/orgs/{slug}/domains/{name}— one domain's local row + a live CF re-fetch (auto-syncs the zone_id when CF surfaces one we don't have yet).GET /v1/orgs/{slug}/domains/{name}/dns— list zone records.POST /v1/orgs/{slug}/domains/{name}/dns— body{type, name, content, ttl?, proxied?, priority?, comment?}. Create A / AAAA / CNAME / MX / NS / TXT / CAA / SRV / PTR / DS / HTTPS / SVCB records.PATCH /v1/orgs/{slug}/domains/{name}/dns/{id}— update record.DELETE /v1/orgs/{slug}/domains/{name}/dns/{id}— delete record.
Wang — the customer-facing agent
Wang is the agent layer described on How Wang works. Two surfaces:
- In-dashboard chat — open tellwang.com/dashboard, the "Wang" card auto-renders after sign-in. First message mints a chat key automatically. Talks to
POST /v1/llm/v1/chat/completionswith the Wang SOUL persona. No setup, no extra deploy. - Per-org Hermes sidecar — for teams that want a standalone agent surface (Slack/Telegram channels, plugins, skills). Lifecycle below.
The sidecar runs in a dedicated Docker container on TellWang's infra, behind an auto-provisioned wang.<slug>.tellwang.com hostname with managed TLS:
POST /v1/orgs/{slug}/wang/deploy— lazy provision. Mints freshsllm_+skl_keys scoped to the org, allocates a host port, docker-runs the upstream hermes-agent image with TellWang env (TELLWANG_LLM_URL,TELLWANG_MCP_URL,TELLWANG_API_KEY,TELLWANG_BEARER,TELLWANG_MODELpicked from plan tier), creates the CF DNS A record, appends the Caddy reverse-proxy block, reloads. Returns{wang_url, status, host_port, container_name, model, plan, llm_key_prefix, mcp_key_prefix, dns_record_id, edge_wired}.GET /v1/orgs/{slug}/wang— current row + reconciled docker state + recent log tail.DELETE /v1/orgs/{slug}/wang— stop container + revoke keys + remove DNS + strip Caddy block. The/datavolume is preserved so a re-deploy gets sessions back.
The in-dashboard chat ships today; the standalone sidecar chat surface is still in progress.
Wang chat persistence
The in-dashboard Wang chat keeps the conversation on the server too, so logging in from a fresh browser brings the thread back. Schema mirrors the OpenAI chat-completions wire format so rows round-trip without lossy re-mapping.
GET /v1/orgs/{slug}/wang/chat— returns the last 200 messages oldest-first as{messages:[{id, role, content?, tool_calls_json?, tool_call_id?, tool_name?, created_at}]}. Role is one ofuser|assistant|tool.POST /v1/orgs/{slug}/wang/chat— append one. Body matches the OpenAI message shape:{role, content?, tool_calls?, tool_call_id?, name?}. Content cap 200 KiB. Returns 201 with the inserted row.DELETE /v1/orgs/{slug}/wang/chat— clear all messages for the org. Returns 204.
No per-call billing — chat history is metadata, not a paid action; LLM tokens are already metered on the sllm_ key.
Org instructions & knowledge base
Teach Wang about your business so it answers with your context instead of generic defaults. Both Wang surfaces — the in-dashboard chat and the per-org sidecar — honor them.
- Instructions — a markdown blob Wang always follows (tone, naming conventions, approval policy, "always provision in region X").
GET / PUT / DELETE /v1/orgs/{slug}/wang/instructions(PUT body{body}, 20 KB cap). Appended to Wang's base persona; it never overrides the safety / boundary-authz rules. Edit it in the dashboard's Wang tab. - Knowledge base — documents Wang retrieves per question.
GET /v1/orgs/{slug}/wang/knowledge(list, bodies omitted),POST(add{title, body}; 100 KB/doc, 200 docs/org),DELETE …/wang/knowledge/{id}, andGET | POST …/wang/knowledge/search?q=(full-text-ranked top matches). Retrieved per-query via thesearch_knowledgeMCP tool (sidecar + any MCP client) and client-side by the dashboard chat.
Retrieval is Postgres full-text search today; semantic (embedding) retrieval is a roadmap upgrade.
Inbound email — Wang can read it
Mail received on a domain attached to a Wok lands in that Wok's inbox; Wang can list and read it to summarize, extract, classify, or draft a reply.
GET /v1/woks/{id}/inbox— list received messages (bodies omitted;{id, from, to, subject, received_at}).GET …/inbox/{message_id}— fullbody_text+body_html.DELETE …/inbox/{message_id}— idempotent.- Wang reaches these through the
list_inbox+read_emailMCP tools. Delivery is the Resend inbound webhook (POST /v1/webhooks/email/inbound, Svix-signed) routed to the Wok by recipient domain.
Channels — Telegram & Slack
Connect a messaging bot so your team talks to Wang where they already are. The bot credential is yours (the @BotFather token / Slack bot token), stored encrypted; no platform-wide bot is involved. Inbound messages run a Wang turn grounded in this org's instructions + knowledge base, and the answer is posted back to the same chat.
GET /v1/orgs/{slug}/channels— list connected channels (kind,token_fingerprint,webhook_url; never the token).PUT /v1/orgs/{slug}/channels/{kind}— connect/rotate.kindistelegramorslack. Body{token, signing_secret?}(signing_secretrequired for Slack). For Telegram we callsetWebhookfor you and return the bot@username; for Slack we return thewebhook_urlto set as the Event Subscriptions Request URL.DELETE /v1/orgs/{slug}/channels/{kind}— disconnect (TelegramdeleteWebhookfirst), 204.- Inbound (provider-called, public):
POST /v1/channels/telegram/{secret}andPOST /v1/channels/slack/{secret}. Authenticated by the random per-channel secret in the path, plus Telegram'sX-Telegram-Bot-Api-Secret-Tokenheader / Slack's HMAC request-signature.
Connect it from the dashboard's Channels tab, or via the connect_channel / list_channels / disconnect_channel MCP tools. v0 answers from a channel are LLM + knowledge-base grounded (great for support/Q&A); having Wang run its full tool-loop in response to a channel message is a roadmap upgrade.
Key management
Generate and manage encryption keys, then encrypt, decrypt, and sign through your Wok — the private key material never leaves the control plane. Key types: aes-256-gcm (encrypt/decrypt), ed25519 and rsa-4096 (sign/verify). An ed25519 key's public key is a Solana address, so /sign is a remote signer for Solana transactions. Bearer-of-org; use $ORG_SLUG or me. See Key Management for worked examples.
POST /v1/orgs/{slug}/kms/keys— create{name, type}. Returns metadata only (name, type, version, fingerprint, public key for asymmetric); the material is never returned.GET /v1/orgs/{slug}/kms/keyslists;GET …/kms/keys/{name}reads one;DELETEis idempotent.POST /v1/orgs/{slug}/kms/keys/{name}/rotate— new version becomes current; older versions are retained so prior ciphertext and signatures still resolve.POST /v1/orgs/{slug}/kms/keys/{name}/import— bring your own key.aes-256-gcm:{material_b64}(32 bytes).ed25519:{secret_key}(asolana-keygenid.json array or a base58 secret key) or{seed_b64}.rsa-4096:{private_key_pem}. The response echoes the derived address so you can confirm it.PUT /v1/orgs/{slug}/kms/keys/{name}/state—{state}enable or disable a key.POST …/kms/keys/{name}/encrypt—{plaintext_b64, aad_b64?}→{ciphertext}.…/decryptreverses it.…/generate-data-keyreturns a fresh data key plus its wrapped form for envelope-encrypting large payloads locally.POST …/kms/keys/{name}/sign—{message_b64}→{signature, signature_b64, version}(theed25519signature is base58, ready for a Solana transaction).…/verifytakes{message_b64, signature}→{valid}.
Model gateway wire format
The model gateway is OpenAI-compatible. Any library that targets the OpenAI HTTP API works against https://tellwang.com/v1/llm/v1 with an sllm_ Bearer.
POST /v1/llm/v1/chat/completions— proxy to the configured upstream (DeepSeek by default; per-org BYOK override viaPUT /v1/orgs/{slug}/llm-byok). Body is the OpenAI chat-completions schema verbatim. On success the response carriesX-LLM-Tokens-Remaining+X-LLM-Requests-Remaining+X-LLM-Source(one ofoperatororbyok:<provider>). 402CP_LLM_INSUFFICIENT_BALANCEwhen both meters hit zero (BYOK orgs skip metering).- Streaming — pass
stream: true. The gateway returns SSE (Content-Type: text/event-stream) and force-injectsstream_options.include_usage = trueon the upstream so the finaldata: {"usage":{...}}chunk arrives for post-stream meter debit.X-Accel-Buffering: nosuppresses Caddy buffering so chunks land at upstream latency. Client disconnect mid-stream still debits the tokens already delivered. GET /v1/llm/v1/models— OpenAI-spec discovery; returns{object:"list", data:[{id, object:"model", created, owned_by}]}. Operator-default orgs seedeepseek-chat+deepseek-reasoner; BYOK orgs see a singlebyok:<provider>pseudo-entry (the actual model name passes through verbatim — any model the upstream accepts works).