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. Successful responses are replayed for safe retries and never double-apply (or double-charge); failed responses are revalidated so a repaired target is not pinned behind an old error. - 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
A newly provisioned Wok includes the standard reference app and GEO foundation: a crawlable /blog/, llms.txt, confirmed-author attribution rules, and an operator-seeded content worker. The Wok's GEO content panel and Wang can configure topics, up to five search languages, a writing language, a dedicated English Reddit search, author attribution, owner notifications, and daily, weekly, or monthly cadence after the owner opts in. The private model is the default research planner: it combines a bounded live-homepage snapshot with the owner's topics to plan language-specific keywords, with deterministic topic queries as the fail-safe. Research plans, runs, questions, and labeled sources are visible in the dashboard; existing Woks are not changed.
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" }Transfer a complete Wok
POST /v1/orgs/{slug}/woks/{id}/transfer with {"dest_org":"destination-slug","include_linked_preview":true} moves the Wok-owned application, database, Storage, Git, RAG corpus, and portable configuration. For a Published Wok, the dashboard moves Published and its linked Preview in one transaction so the pair cannot split across organizations. The same signed-in human must control both organizations; a source-side approval is not destination consent. Managed RAG and GEO model credentials are replaced with destination-owned keys and the source keys are revoked, so later use cannot spend the source organization's allowance.
The boundary deliberately excludes human and organization authority. Source chat and approval history stays in the source organization; member OAuth tokens are revoked; payment gateways and phone routes are disconnected; pending OAuth consent is cancelled; and a marketplace listing becomes unpublished. App-user OAuth connections remain with the app as customer data. Connected hostnames move with the stable Wok id and stay live. Exact matching TellWang-managed DNS or registration ownership moves too; broader parent zones stay with the source. Active runs or deployments still block the transfer.
Diagnostics
Three layers, from cheapest to most detailed. All return JSON.
GET /v1/healthz— liveness only.200 {"status":"ok"}when TellWang 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). Personal-model readiness appears aschatgpt_oauthandgrok_connection; these checks verify root-key availability without reading or exposing a member's credentials.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, and the latest runtime-heal artifact invariant. A missing-runtime repair recreates infrastructure from persisted state and must preserve source, project context, migration lineage, frontend, and functions;heal_artifact_integrityreports the proof or compensation result. 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 optional site_capture diagnostic reports disabled
without degrading the platform when neither capture-worker secret is
installed. Partial configuration is degraded; an enabled worker must
pass its health check, including the worker's authenticated and
bounded Browsertrix oracle-contract-v1 probe, advertise public manifest
contracts v1 and v2 with the full 200-route and 512 MiB grants, and prove that its current storage
namespace resolves through the configured backend registry to the
worker generation that owns it. The check reports worker-active and
queued jobs separately, plus active capacity and the crawler's
memory-pressure budget, so an admitted queued segment is not
presented as browser work already in progress.
shopify_post_edit_commerce reports disabled when both
harness secrets are absent, because imported Shopify Git edits then
fail closed. When enabled, it requires the authenticated harness to
use an exact loopback origin, return the pinned health contract, and
have a valid migration-receipt verification keyring.
The semantic distinction: /healthz answers "should the load balancer keep sending us traffic"; /diag answers "what's right and what's broken, with evidence." TellWang's automated health monitoring 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's stable service base is{ "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", "rag_retrieve_url": "https://tellwang.com/v1/llm/v1/rag/acme-pos/retrieve", "rag_agent_url_template": "https://tellwang.com/v1/llm/v1/rag/acme-pos/agents/{slug}/chat", "redis_url": "redis://default:<dbpass>@redis:6379" // internal-only, edge fn use }https://<wok-id>.tellwang.com, 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. When a verified custom domain is bound, website paths on the platform hostname permanently redirect to that canonical domain; the service paths remain available. TellWang also aligns generated robots and sitemap URLs, canonical links, andog:urlwith the custom origin. Edge functions that returntext/event-streamare proxied unbuffered for SSE, and WebSocket Upgrade handshakes pass through the same functions route. 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 TellWang.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. When RAG is enabled, customer-facing functions receive a private model-gateway key and call the returned gateway URLs; see Functions → Managed RAG.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— reconcile asuspendedWok from its declared Compose stack and retained volumes, including recreating containers removed by host cleanup. 200 if already active; 409 if destroyed.POST /v1/woks/{id}/wake— start a Preview Wok that idled out and wait until its services answer. Preview Woks sleep after a few hours unused; any build request wakes one automatically, and this is the explicit lever when you want it awake before you look at it.POST /v1/orgs/{slug}/platform-problems— report a problem with the platform itself: a documented command that does not exist, a tool erroring through no fault of the call, a Wok or release stuck with no forward move, or docs that do not match reality. Agents reach it aslollipop report-platform-problem. Reports deduplicate by fingerprint, so repeated hits raise a count rather than adding rows, and TellWang attaches the Wok status, the last deployment error and the run's recent tool calls itself.GET /v1/orgs/{slug}/platform-problems— the sweep: open reports first, most-hit first.?status=anyincludes resolved ones.DELETE /v1/woks/{id}— tear down the stack + volumes and mark it destroyed. Deleting a production Wok also removes its linked staging Wok, so no preview stack is orphaned. Control-plane environment variables and secrets remain until the separate purge step; export anything you need before deleting.POST /v1/woksreturns only after the linked staging Wok is semantically ready and seeded: Postgres accepts SQL and the rendered REST/Auth/Functions/Storage listeners answer, together with Realtime when it is enabled. A fresh Preview copies Published's schema only, never rows, verifies the exact application-schema fingerprint, and then adopts the same immutable migration names and checksums append-only. It does not replay historical DDL during initial creation. A database-backed lease serializes same-ID lifecycle attempts, including stale recovery, so concurrent cleanup cannot remove the winning stack. The server bounds production plus automatic staging at 150 seconds; clients should allow 180 seconds. Network and mandatory-port reservation fail closed before a partial stack is returned. A staging failure includes a stablestaging_error_code, a credential-scrubbed phase hint, and sanitized per-service checks instead of a generic “did not converge.”
Lollipop public-audit handoff
The public Lollipop audit stores the exact approved keywords, prompts, findings, and evidence before account creation. After sign-in, POST /v1/orgs/{slug}/lollipop/onboarding/claim/preview verifies the two-hour handoff token and shows what will be added. POST /v1/orgs/{slug}/lollipop/onboarding/claim with {"lead_id":"…","lead_token":"…","project_id":"…"} creates or matches a watch-only Site, copies the retained Audit, and creates draft Fixes for actionable findings. The claim is idempotent, so a retry returns the same receipt. The public audit receipt is Included · $0.00. “Start watching” collects or reuses the Project's saved sampling_location, creates the Site, and immediately starts its included first audit. Failed attempts retain their safe error and retry receipt without burning that exemption. A legacy Site without a completed report exposes the same first-audit action once it has a public domain or Published address; otherwise the customer is directed to publish or connect the address first. Later audit model judging uses the monthly model allowance first, then the prepaid balance, and the serving model is retained with the report.
The Project selector lists every Project the signed-in account can reach. It includes current-account Project records and keeps legacy Projects stored in another authorized workspace visible; selecting one refreshes into its exact authorized scope. The signed-in canonical path is /app/<account>/projects/<project>/…, so switching two Projects in one account changes the address, and refresh, back/forward, bookmarks, and shared links restore the exact Project. Older account-only links remain readable and are replaced with the exact Project path after bootstrap. Platform workspace names remain hidden behind the customer-facing Account → Project → Site model. Re-adding a removed watch-only domain restores the original Site, audit history, and first-audit ledger in the selected Project instead of issuing another included audit. The dashboard rail keeps daily Project tools above a labeled Manage divider and Files, Domains, Billing, and Members below it. Receive payment is a separate Project tool: it shows only that Project's collected money and Stripe readiness, and creates a fresh single-use Stripe setup link for an Owner, Admin, or Billing member. Lollipop subscription charges and usage remain under Billing. Move this site remains available on Overview before, during, and after the first audit. A native destination uses POST /v1/orgs/{slug}/lollipop/sites/{site_id}/move with {"project_id":"…"}; the Site and Site-bound work cross atomically. A managed Site moving to another authorized workspace uses the existing Site-transfer boundary. Both require owner/admin control. Active work blocks a cross-workspace move, while connected hostnames move with the Site and stay live.
Watched-site import starts with an explicit choice and a separate confirmation screen. While the start request is pending, the confirmation stays open and shows Starting import…; a rejected start remains visible with a retry message instead of disappearing. After acceptance, the durable Site banner shows import progress and restores it after navigation or refresh. POST /v1/orgs/{slug}/lollipop/sites/{site_id}/import accepts {mode:"whole_site"|"content_only"}. Whole-site import remains the durable clone, provision, asset-copy, and staged-v1 job. Its styles, images, fonts, and other passive presentation assets are retained with the static Site release, remain available while Preview services sleep, and must all complete before staged v1 can become ready. Extensionless image and stylesheet endpoints must return a supported media type; an HTML fallback stops the import. Scripts and SVG files that are deliberately not copied keep an absolute source URL instead of being misrouted into the imported Site. Shopify and Shopline are never cloned as ordinary full sites: that mode records commerce_required and returns CP_LOLLIPOP_IMPORT_STOREFRONT_REQUIRED. Content-only import may read those public pages. It retains the complete captured page source, provisions a private Preview, copies the source photos into durable static Site storage, and records every page, copied photo path, alt text, and readable content excerpt in the versioned creative brief. The New Site build re-reads every retained page, reuses that exact Preview, and keeps the content and photos while replacing the layout. CSS, scripts, tracking, commerce behavior, and the old visual structure are not reused. The import stops instead of reporting success if discovery was capped, a page was truncated or skipped, or any passive asset copy is incomplete. Neither mode changes the public site before owner review. A platform administrator may restart a stopped watched-Site import or explicitly reimport a ready managed Site through POST /v1/admin/lollipop/orgs/{slug}/sites/{site_id}/import/retry. A capture that failed before banner polling observed it also counts as stopped. Managed reimport additionally requires the existing Published, Preview, and retained-clone bindings, preserving the Site and Fix history. The action preserves the selected mode, is audit-recorded, and only reconnects the capture to this same customer-visible workflow; it grants no access to other Site actions. For production acceptance, POST /v1/admin/lollipop/orgs/{slug}/users/{user_id}/operator-sessions creates a separately revocable 30-minute test session bound to an existing member's current role. Its launch credential is delivered in a URL fragment, removed before the first request, and kept only in that browser tab; POST /v1/admin/lollipop/operator-sessions/{session_id}/revoke ends it immediately. Both actions are audit-recorded, and neither adds the operator to the account.
Lollipop's New Site → I already have an estore path consumes the platform contracts directly. Connect commerce starts an idempotent intent:"commerce_only" journey, provisions its empty Preview Wok, and opens only the server-bound Shopify approval URL; it imports and generates no pages and requests no theme scope. Quick clone starts capture_profile:"quick", then polls the retained two-route capture until the homepage and one product-page reference are complete or fail visibly. Retries reuse the same browser-session operation key. Both actions require the person to assert ownership or copying authority, and neither changes the source theme, settings, domain, or shopper traffic.
Lollipop redirects are staged on Preview. POST /v1/orgs/{slug}/lollipop/sites/{site_id}/redirects adds a validated 301 rule and proves that its exact Preview source returns 301 with the expected destination in Location; an ordinary 200 page cannot certify the release. DELETE …/redirects/{redirect_id} removes one after confirmation, and POST …/redirects/verify checks every retained rule against Published without following the redirect. Adding or removing a rule creates a staged release; neither changes Published until the ordinary reviewed publish flow. Existing static Sites with a _redirects file receive the corrected path-routing block during startup reconciliation.
Wang-assisted Email replies use two separate Site-scoped actions. POST /v1/orgs/{slug}/lollipop/sites/{site_id}/email/inbox/{message_id}/draft-reply/estimate with {"project_id":"…","objective":"…"} verifies scope and returns the one-request token estimate without calling a model. The idempotent …/draft-reply action additionally requires "confirmed_model_action":true, makes one metered private→DeepSeek→Kimi completion, and returns an editable draft plus serving-model provenance. Neither route sends email, and only non-draft Project/company knowledge is supplied to the model.
Completed Lollipop audits can be shared as frozen public reports. GET /v1/orgs/{slug}/lollipop/sites/{site_id}/audit-share?project_id={uuid} previews the report and POST to the same path with {"project_id":"uuid"} creates the link; DELETE …/audit-share/{share_id} revokes it. The Project selection binds the snapshot to its latest completed AI-answer run, including the approved prompts, short answer excerpts, each answer's safe citation title, domain, and URL list, separate mention and citation percentages, and unavailable-engine receipts. The in-app preview groups every buyer question in an accordion; expanding one shows its frozen answer excerpts and retained links under Answers and citation sources. The public report renders those retained links below their exact answer; an answer with no returned source says so. Creation and revocation require an owner or admin. Share links open through GET /share/{share_token}, whose server-rendered Open Graph and Twitter metadata includes the exact frozen score, measured check counts, and up to three quick verdicts. Its 1200×630 PNG score card comes from GET /v1/lollipop/reports/{share_token}/card.png, so messaging apps do not need to run JavaScript to build the preview. The report-data response also returns the authoritative share_url; the public report restores that crawler-facing address in the browser and copies it instead of the metadata-free application route. The edge redirects a previously copied /report/{share_token} address to the same versioned share page before a messaging crawler reaches the application shell. Newly returned page and image URLs include a stable preview-version query to bypass older messaging-client caches. Valid metadata and cards permit a five-minute messaging cache while the platform edge still checks the active link on every request. The signed-out report-data route stays no-store. All three public routes are rate-limited, non-indexable, and return the same neutral unavailable response after revocation. The database stores a token hash, not the raw URL token.
Lollipop AI Answers runs from the control plane and is scoped to one Site at a time. The Site selector changes the buyer-question set, sampling history, mention rate, citation rate, and excerpts together; another Site in the same Project is never used as a fallback. GET /v1/orgs/{slug}/lollipop/mentions/readiness?project_id=…&site_id=… reports that Site's question count, the Project's saved market, and whether the server-owned OpenAI web-search, DeepSeek Responses web-search, DataForSEO Google AI Overview, native Naver AI Briefing, and DataForSEO Naver Organic collectors are ready without returning credentials. Each source is identified as an answer or search source. PUT …/mentions/settings verifies the selected Site and maps the Project's country, city, or region to a canonical DataForSEO location before sampling. POST …/mentions/queries requires the selected site_id, and idempotent POST …/mentions/sample queues a durable run for only that Site. A missing Site or ambiguous market is rejected before queueing or charge. Every source request includes that saved market: answer models must resolve ambiguous place names within it, and search requests include it in the query. DeepSeek is told to treat that market as authoritative, avoid search-process narration, and return the exact public URLs it cites. Lollipop retains both its structured citation annotations and explicit HTTP(S) links in the returned answer, but does not turn uncited internal search results into citations. Google also receives the matched numeric location code. Native Naver AI Briefing keeps its displayed answer and sources, while Naver Search creates and polls a DataForSEO task and retains only returned organic rows. Naver Search remains visible as SEO evidence but is excluded from AI-answer percentages, check 05, public answer snapshots, and AI share of voice. Every question-and-source check retains a success or classified failure receipt. A business name repeated from the question does not count as an answer mention; a returned source title or matching source domain still counts independently as a citation. Each sample also retains every returned citation plus every match against the Project's explicitly tracked competitors. The Site audit report and Project AI Answers show each answer's exact safe citation links and every matching competitor occurrence without relabelling unrelated sources. Each successful question×source check costs $0.01 from the prepaid balance; a provider failure is refunded. OpenAI and DeepSeek output is identified as the corresponding API web-search result rather than claimed to reproduce a person's consumer app session, while Google AI Overview and Naver Search store the exact result types returned by DataForSEO.
Lollipop unreadable-Site reports
If a public homepage refuses or cannot provide readable HTML, the Site audit completes as 0/100 · Not readable by AI. It stores no invented buyer questions, answer samples, page metrics, or charge. This unreadable result does not consume the included first readable audit.
For a readable Site with completed answer samples, the headline score assigns 60% to the crawl and content result, 20% to the mention rate, and 20% to the citation rate. A numeric section needs 60/100 to show Looks good. If every answer source fails, answer visibility remains unmeasured and does not become an invented zero. Retained reports use their saved crawl result and exact answer counts, so corrected scoring does not require another paid audit.
Lollipop first-site creative brief
The no-site wizard creates a durable draft with POST /v1/orgs/{slug}/lollipop/site-briefs, autosaves exact revisions with PATCH /site-briefs/{brief_id}, and uses explicit /analyze, /inspiration, and /recommend-style actions for page planning, sanitized palette extraction, and design-language ranking. These authenticated creative actions use the monthly model allowance first, then the prepaid balance. They try the private model first and may fall back to a metered DeepSeek or Kimi route after an explicit provider failure; every stored result names the serving model and that it was not exempt. The pre-account audit and first audit of every Site remain separate $0.00 acquisition paths. POST /v1/orgs/{slug}/lollipop/sites/{site_id}/audit-questions crawls the Site and prepares one to six inferred buyer questions without sampling an external visibility source. The questions are phrased for a cold buyer who knows their need but has not discovered or selected the business. Site-specific evidence determines what the business can answer; branded packages, staff processes, and assumed visits do not become knowledge attributed to the buyer. The owner edits and confirms those questions on a separate screen; only then does POST …/audits/run accept the draft and sample configured sources. Completed reports expose Run audit again and use the same review screen. Before a repeat starts, the app shows approved questions × ready sources, the dollar estimate, and model-funding terms; the API refuses an unconfirmed paid rerun. Repeat runs retain a visible running state across navigation and never mix questions from another Site. The included first audit exempts every call; later audits remain metered. GEO Optimize consumes this completed audit evidence and does not call ChatGPT, DeepSeek, Google AI Overview, or Naver directly. Model analysis continues against the saved brief while the app polls, so an edge deadline or closed browser cannot erase valid owner input. Style matching persists recommendation_state until the metered AI result or accessible catalog fallback is ready, so the open wizard does not show a stale preliminary match. GET /style-catalog returns reviewed semantic tokens pinned to an immutable MIT-licensed source commit. POST /site-briefs/{brief_id}/builds freezes the owner-approved revision and starts a live Preview-only build on the all-plan DeepSeek Flash coding route without changing the member's normal chat preference; the Agent build uses the monthly model allowance first, then the prepaid balance. During work, the exact active run records preparing-Wok, page, function, design-system, interaction-test, and Preview-verification stages through POST /site-briefs/{brief_id}/builds/{build_id}/progress. Sites reloads those stages and any pending owner fact or permission request after navigation or browser close. The Agent Cell must finish with POST /site-briefs/{brief_id}/builds/{build_id}/complete; the server accepts only that run's exact latest verified Preview receipt and returns its summary, passing checks, and frozen brief for owner review. It never publishes. If that run stops, the failed attempt remains visible and the owner may retry the exact frozen brief or edit a new revision.
The Look & feel stage accepts up to three mixed website and image references in the autosaved brief. Website URLs guide design inspection during the build. Images are decoded, sanitized and reduced to palette and mood notes; raw bytes are never stored. References never authorize copying branding, assets, text or pages. The stage also provides idempotent POST /site-briefs/{brief_id}/palette for explicit private palette derivation from the written brief and up to three preferred colours. It returns the same five semantic roles and serving-model provenance as image analysis; autosave never calls a model. DELETE /site-briefs/{brief_id}/inspiration clears inspiration provenance after the owner decides whether the already-saved derived colours should remain. After an owner selects a verified style, idempotent POST /site-briefs/{brief_id}/refine-style accepts one explicit instruction and stores only allowlisted semantic colour, density, border, heading and motion overrides with parent slug, catalog commit, exact diff, serving model and billing provenance.
Lollipop context now includes exact competitor citation titles from the latest applicable answer sample, associated only when the saved competitor name or domain matches that citation. A Search and AI visibility run must evaluate every approved buyer question against the Site's current relevant pages, its mention and citation result, and those exact titles. Supported gaps become content.update Fixes when a suitable page is incomplete or page.create Fixes when no suitable page exists; a run that proposes neither records its evidence-based reason. Competitor material is a research lead for topical coverage and answer structure, not a source of copied wording, branding, or claims. When an owner-only fact is missing, the Fix stores the question and links each dependent change through needsQuestion, so execution waits for the saved answer instead of inventing it or dropping the opportunity. An approved page.create Fix calls POST /v1/orgs/{slug}/lollipop/plans/{plan_id}/runs/{plan_run_id}/article-drafts. The server binds the article to that active execution and Site, requires the exact citation or knowledge titles used, runs the content-quality gate, and places the result in Content for owner review. It never publishes the article or silently writes it into Preview. Every Fix opens at a durable direct route, keeps its background attempt and friendly terminal failure across refresh, and can retry a stopped attempt. When a reimport or another verified deployment supersedes a ready review, the retained Fix offers Reapply fix against the current Preview and preserves the earlier attempt for audit. A startup failure restores the exact waiting Fix instead of leaving a false applied state. Preview verification asserts the approved change and real page behavior. Inherited third-party script, resource, and page errors remain visible warnings when the page renders and every target assertion passes; navigation failure, a blank render, or a failed target assertion still blocks the Fix.
The Optimize wizard saves each person's current question and answers under GET|PATCH|DELETE /v1/orgs/{slug}/lollipop/projects/{project_id}/optimize-draft. The draft follows that person across browsers without becoming shared Project configuration. The server bounds every field and verifies that the selected Site and optional edit target still belong to the Project. Changing a completed goal requires confirmation before replacing its dependent outcome; Review links return to the exact question and then back to Review. The frequency step shows the selected Site's current approved-question × ready-source count, maximum provider charge, and monthly-allowance-then-prepaid model funding for a required stale-audit refresh. “Ask Wang to help me choose” first calls the unmetered /optimize-recommendation/estimate route and discloses one ordinary metered request. Only a separate confirmed action calls /optimize-recommendation; it uses approved Project/Site/audit evidence, returns an allowlisted answer with reason and confidence, and never inherits the first-audit exemption. Starting clears the draft only after creating an enabled optimization, freezing plan version 1, and immediately queueing its first durable Agent Cell run. Reviewed edits append another immutable plan version and explicitly do not start a run; Run now and the stored schedule remain the execution paths.
Each optimization stores one explicit target_site_id. That Site is enforced at the Project-context read and Fix-write boundaries, so an optimization cannot widen itself to the Account's other Sites. Daily, weekly, fortnightly, and monthly runs are claimed durably; “After each publish” queues only after a verified Published release. Run now and recurring runs first check the selected Site's latest audit. Evidence older than 24 hours is refreshed through the ordinary authenticated Site-audit route with the last owner-confirmed buyer questions and normal repeat-audit funding; a Site without approved questions stops at the question-review step instead of inventing them. Every recurring run re-checks its human owner's current Project capability and uses a short-lived credential that is revoked immediately. A verified publish now freezes an outcome window for every newly published Fix in that release, including its expected metric, direction, horizon, pre-release Site/audit reference, and first check time. Project context returns these windows to the selected-Site optimization. A report uses measured_plan_ids only for windows its evidence actually measures and plan_ids only for new draft Fixes. The server rejects cross-Site or unpublished links, retains inconclusive evidence honestly, and links the first next opportunity without executing or publishing it. Leaving Optimize or closing the browser disconnects only the live progress stream. Reopening Optimize reattaches to the retained run and reports queued, working, Waiting for you, complete, or failed state. Pending questions expose Send reply, and permission requests expose Allow once or Reject. Pause/resume and Run now are immediate lifecycle controls; Remove stops future work while retaining plan, run, and report history. The Agent Cell receives the exact structured report command, must place every report JSON value in a writable /tmp file instead of retrying brittle shell quoting, and is explicitly banned from using durable Fixes as schema probes. Reports show the control-plane-assigned comparison period, measured outcome, before/now metric when available, supporting evidence, and the next useful measurement time when evidence is insufficient. Their customer-facing title is derived from the optimization and selected Site, so model-written database or platform identifiers cannot appear in the report or Feed label. The first accepted report atomically completes its run. A confused retry returns the original report receipt and a direct instruction to stop without adding another report or Feed event, and startup recovery closes a reported run left active by a control-plane restart. Verified Preview receipts are derived from completed Fix executions; an optimization cannot invent them in its report.
Organization people
The dashboard's dedicated Billing → People page lists everyone with access to the current organization. Owners and admins can add a person by email and choose a role. Email identity is case-insensitive and stored in canonical lowercase, including when a previously removed member is added again with different casing. Removing membership does not delete the reusable account. Adding a person does not send an invitation email; if the address does not have a TellWang account yet, the person can sign up with that same address to claim the preassigned membership.
GET /v1/orgs/{slug}/users— list organization members.POST /v1/orgs/{slug}/userswith{"email":"teammate@company.com","name":"Teammate name","role":"developer"}— add or update a member. Owners can assign any role; admins cannot assign the owner role.DELETE /v1/orgs/{slug}/users/{user_id}— remove a member and immediately revoke their active organization keys. The Billing → People page asks for confirmation first. Admins cannot remove owners, and TellWang refuses to remove the last owner.
Workspace Feed
GET /v1/orgs/{slug}/feed returns the owner-facing workspace activity stream and a lifecycle summary for every non-destroyed Wok. Feed items cover customer email, signups, connected business sources, reviewable drafts, GEO audits, provisioning milestones, and connected domains. The overview object reports Published and Preview counts plus each Wok's current active, suspended, pending, or provisioning state. The dashboard uses this to keep sleeping Preview and in-progress setup visible instead of presenting only a flat event list.
Dismiss an item with POST /v1/orgs/{slug}/feed/dismiss {"ref":"…"}. Dismissal is organization-scoped and idempotent.
Deep copy: grounded site rebuilds
POST /v1/orgs/{slug}/storefront-platform/detect accepts
{"source_url":"pretti5.com.hk"}, normalizes a bare
domain to HTTPS, and returns a bounded Shopify, Shopline,
WooCommerce, or unknown observation with evidence and confidence. A
confirmed Shopify result also includes its permanent
myshopify.com identity and a stable evidence digest for
the publicly detected classic or new
customer-account mode. A committed migration cannot create its
Preview until those discovery signals are locked. A confirmed
Shopline result includes the permanent myshopline.com
identity observed from the source domain or runtime. A confirmed
WooCommerce result also includes its same-origin WordPress REST root,
whether the site uses /wp-json/ or plain-permalink
?rest_route=/ discovery. Detection is a source probe, not
merchant authorization or migration readiness. The Shopify account
digest excludes dynamic page bytes, so an unchanged account mode has
a repeatable receipt.
POST /v1/orgs/{slug}/site-clones captures bounded
evidence from an existing site for a clean rebuild. It recursively
expands same-origin sitemap indexes and balances the explicit page cap
across sitemap groups. Raw mode captures up to 60 pages;
render:true captures up to 30 post-JavaScript pages in
the background. Each page is capped at 2 MiB and reports whether it
was truncated. This is presentation ground truth, not a claim that the
whole site, database, user accounts, inventory state, cart, payment
flow, or checkout was imported.
The dashboard exposes bounded Shopify, Shopline, and WooCommerce
public capture plus committed Shopify or Shopline
live_transition setup in the top-level
Import workspace. A Shopify or Shopline journey can
provision a journey-unique pair or select a fresh empty unassigned
owned Preview, persist and recover its current action, and begin
server-handled provider authorization. A Shopline journey securely
stores its rotating Admin grant and app-owned Storefront token, then
continues through provider-specific extraction and the shared capture,
execution, Preview, evidence, parity, rollback, and handoff path. A
detected WooCommerce source is
directed to Tryout before journey creation. The three Shopify
connections, fixed worker, compatibility
runtime, internal Preview deploy bridge, deployment-bound browser and
commerce evidence producer, fail-closed parity gate, and editable Wok
handoff are integrated behind signed, lease-bound contracts. Merchant
authorization, a complete route universe, browser evidence for every
non-catalog route, locale-matched product and collection template
evidence, a digest-bound catalog route set,
Pretti5-specific app/account/checkout evidence, independent QA, and
owner review must still pass; neither a capture receipt nor a stored
connection is a migration-ready claim.
Storefront connections and committed journeys
Organization Bearer routes expose safe connection metadata through
GET /v1/orgs/{slug}/storefront-connections. Begin the
browser-bound Shopify installation and full-migration OAuth journey
with
POST /v1/orgs/{slug}/storefront-connections/shopify/authorize,
begin Shopline authorization through the exact assigned custom app or reviewed public app with
POST /v1/orgs/{slug}/storefront-connections/shopline/authorize,
provision the normal managed Storefront runtime with
POST /v1/orgs/{slug}/storefront-connections/{connection_id}/shopify/storefront-runtime/managed,
detect and bind the live store's classic or new customer-account mode to the exact Preview with
PUT /v1/orgs/{slug}/storefront-connections/{connection_id}/shopify/customer-account,
or reload its secret-free setup by account connection ID with
GET /v1/orgs/{slug}/storefront-connections/{connection_id}/shopify/customer-account,
WooCommerce read-only application authorization with
POST /v1/orgs/{slug}/storefront-connections/woocommerce/authorize,
or signed WordPress connector pairing with
POST /v1/orgs/{slug}/storefront-connections/woocommerce/connector/authorize.
The WooCommerce routes are dormant connection foundations and are not
exposed as a startable live-migration customer flow in this iteration.
TellWang keeps the generated single-store install link in platform
secrets. Its same-origin launch verifies the requested store and
client before Shopify opens; Shopify returns to a signed installation
callback that automatically continues into the exact
pretti5-full-migration.v1 grant. The authorization response
includes the permission-contract version and SHA-256, and the current
journey action discloses the complete scope set before approval. The
matching public callback routes validate one-use state or
challenge evidence before encrypted credential storage. Shopify also
binds the initiating browser with signed HttpOnly cookies, then returns
303 to the validated relative return path with only the
non-secret storefront_connection ID. At most ten unconsumed
authorization states may exist per organization and provider; a capped
start returns 429 with Retry-After: 60.
One installed TellWang Shopify app/store pair may have one active or
revoking TellWang organization owner. A callback from another
organization fails before Shopify token exchange with
CP_STOREFRONT_SHOPIFY_STORE_ALREADY_CONNECTED. Keep using
the owning organization, or finish disconnect and provider revocation
there before authorizing the store elsewhere.
Emergency-disconnect a connection family with
DELETE /v1/orgs/{slug}/storefront-connections/{connection_id};
this terminates migration leases, removes both reserved Shopify Wok
environment bindings, scrubs stored credentials, and restarts every
active linked Preview and Published Wok. The family reaches
revoked only after every restart succeeds. Shopify-side app
removal remains visible as provider_revocation_pending;
the merchant must uninstall TellWang in Shopify Admin to finish
provider-side cleanup. Shopify sends signed
app/uninstalled and app/scopes_update events
to POST /v1/storefront-connections/shopify/webhooks so the
reconciler can scrub a revoked or scope-drifted connection family.
Additional merchant-restricted Shopify apps may coexist with the
primary app through the bounded platform-secret registry. Each app
has a stable custom:<slug> identity, distinct
credentials, and one Shopify-generated install link bound to one
permanent *.myshopify.com store. TellWang selects that app
only for its assigned store and preserves the issuing identity for
callback, refresh, lifecycle, and revocation checks. Registry values
never enter a merchant form, Wok environment, response, or log.
No storefront-connection MCP
tools ship in this slice.
/diag reports shopify_migration_oauth as healthy
only when the managed app credentials, every configured custom-app
assignment, exact HTTPS callback, and control-plane encryption key are
available. Authorization and callback
responses are non-cacheable and use a no-referrer policy. Shopify must
return an expiring offline token with the exact full-migration scope
set; the credential is retained only in its encrypted connection
envelope.
Shopline uses the same connection boundary through either the exact
store-bound custom app or the separately reviewed public app. Their
signed install and OAuth callbacks live at
GET /v1/storefront-connections/shopline/install and
GET /v1/storefront-connections/shopline/callback. Signed
lifecycle events post to
POST /v1/storefront-connections/shopline/webhooks; required
customer and merchant redaction events post to
POST /v1/storefront-connections/shopline/compliance.
Because current Shopline webhook versions are managed through the
Admin API, TellWang verifies or creates the exact uninstall
subscription during OAuth and fails the connection closed if that
receipt is unavailable.
shopline_migration_oauth and
shopline_oauth_refresh report app configuration and
ten-hour credential renewal health. TellWang stores the Admin grant
and app-owned Storefront token encrypted and returns only secret-free
connection metadata.
The same restricted app can admit its assigned Pretti5 store plus one
lowercase development-store hostname from operator-owned configuration;
the current canary entry is pretti6.myshopify.com. Before
retaining a grant, TellWang verifies Shopify's immutable Shop ID and
permanent domain through Admin GraphQL. Background renewal stays bound
to that grant, and signed lifecycle events must match the stored Shop
ID. A separate public app is being prepared for Shopify App Store review and will provide the marketplace path after approval.
The Admin scopes are read_themes,
read_products, read_product_listings,
read_publications,
read_online_store_navigation,
read_online_store_pages, read_metaobjects,
read_metaobject_definitions, read_markets,
read_locales, read_translations, and
read_legal_policies. The Storefront scopes are
unauthenticated_read_product_listings,
unauthenticated_read_product_inventory,
unauthenticated_read_product_tags,
unauthenticated_read_content,
unauthenticated_read_metaobjects,
unauthenticated_read_selling_plans,
unauthenticated_read_checkouts,
unauthenticated_write_checkouts,
unauthenticated_read_customers, and
unauthenticated_write_customers. The Storefront writes
are shopper checkout and account operations. No Admin write, order,
payment, domain, or traffic scope is accepted.
This is one Shopify app approval, not one generic credential. The
broad OAuth token remains encrypted in the control plane. The
migration worker receives only fixed operations for resolving the
published MAIN theme, reading bounded theme-file pages,
and verifying the same snapshot. TellWang also creates or reuses an
app-owned public Storefront token inside the control plane, verifies
its exact ten-scope contract, and stores it encrypted for the
protected Wok server. The merchant does not install Headless or paste
a token for this managed Storefront runtime; stores using newer
Customer Accounts may still need Headless-channel public-client
setup. The runtime connection reports
auth_mode: managed_public_token.
CP_STOREFRONT_SHOPIFY_MANAGED_RUNTIME_UNAVAILABLE means
provisioning can be retried without another approval;
CP_STOREFRONT_SHOPIFY_MANAGED_RUNTIME_INVALID means Shopify
did not return the exact runtime contract; and
CP_STOREFRONT_SHOPIFY_MANAGED_RUNTIME_NOT_EXTENDABLE means
a TellWang operator must publish a storefront-capable, extendable app
version and retry with the existing merchant approval. The control
plane renews the rotating offline grant five minutes before access
expiry and records a secret-free bounded retry schedule. The
shopify_oauth_refresh diagnostic degrades for expired or
near-expiry grants and renewal backlog. Transient renewal and managed
runtime failures retry with the existing approval. A merchant
reconnect is requested only after the offline grant is definitively
revoked or expired. That path archives secret-free lineage, removes
stale capture and execution evidence, and returns the same journey to
source authorization. A future permission expansion receives a
deliberate new approval. The first production
canary must prove that the distributed app can execute
storefrontAccessTokenCreate; Shopify may require the app
to be extendable. The legacy
PUT …/shopify/storefront-runtime private-token route is
retained only for immutable MigrationTemplate 1.0.0
compatibility and is not part of the normal Import journey.
The Customer Account row is mode-specific. The server probes the live
journey's public /account route and binds a digest of that
evidence. Classic mode reuses managed Storefront access, accepts no
public client or OAuth URLs, and
generates only a sealed Wok session key. New mode requires a public
Headless Client ID, binds the exact Preview callback/origin/logout
URLs, and verifies Authorization Code with PKCE S256 discovery. Its
GET returns only secret-free setup metadata, including
account_mode, auth_mode, and
mode_verified. No Storefront token, buyer password, customer
access token, or session key is returned.
Mode detection or discovery is setup evidence. A merchant-assisted real Preview login, logout, order-history, and signed-in checkout canary must still pass. Exact imported account-page layout and behavior are a separate launch blocker; a generic working account page is not source parity.
Managed Storefront verification reads shop, navigation, product, inventory,
tag, page, and blog data, then creates an empty cart and validates its
HTTPS checkout URL. The signed Shopify Marketplace template requires
unauthenticated_read_checkouts,
unauthenticated_write_checkouts,
unauthenticated_read_product_listings,
unauthenticated_read_product_inventory,
unauthenticated_read_product_tags,
unauthenticated_read_content,
unauthenticated_read_metaobjects,
unauthenticated_read_selling_plans,
unauthenticated_read_customers, and
unauthenticated_write_customers. The managed token stays
in protected server configuration. Safe retries reuse the same
app-owned token and do not reopen Shopify approval.
POST /v1/orgs/{slug}/storefront-migration-journeys
starts a committed Shopify or Shopline v1 journey with a stable
Idempotency-Key, source URL, rights assertion, and either
a new or existing Preview target. intent:"live_transition"
follows capture, compilation, Preview, evidence, and parity.
Shopify also accepts intent:"commerce_only": it binds the
protected Storefront commerce configuration and returns
page_strategy:"custom" without capturing, importing, or
generating pages, routes, a runtime definition, or a launch.
A new target receives a server-owned journey-unique Wok name;
an existing target must be active, empty, and unassigned. Completion
accepts only the exact linked Preview returned by provisioning.
Collection and item GET routes recover secret-free
journey state. shopify-adapters/preflight refreshes the authenticated,
root-owned adapter inventory and returns only required keys, availability,
and accepted SHA-256 pins. Missing adapters remain platform-owned work.
When a connected development store returns a password page,
POST .../{id}/shopify-storefront-access accepts its
Online Store password over HTTPS, exchanges it server-side for
Shopify's temporary storefront session, including the cookie-bound
authenticity-token form and updated essential session required by
development stores, discards the raw password,
seals the session to that organization, journey, connection, and
exact store host, then restarts the same failed capture. It does not
put the password in a URL, public receipt, Wok environment, or Wok
Git. The dashboard supplies a private idempotency key so a successful
network retry does not exchange the password again.
Item resume, cancel, and current
action begin/complete routes enforce
server-owned transitions; deletion is allowed only after a terminal
cleanup-eligible state. Shopline completion pins the exact encrypted
connection and starts its server-owned source inventory and reference
capture. A complete capture is promoted into the same immutable
execution lineage used by Shopify; provider-specific receipts prevent
cross-provider replay. The server rejects
WooCommerce or unknown
sources before journey creation; those sources remain available through
public Tryout capture. Completing Shopify authorization accepts only an
active same-organization connection for the canonical store, pins its
secret-free digest, and creates one idempotent discovery capture plus
a durable aggregate bound to the exact organization, Preview Wok,
canonical source, Shopify connection digest, and authorization action.
A sealed route universe can contain up to 20,000 routes; deterministic
segments contain at most 20 routes each, with an independent bounded
network budget. Journey reads retain the flat
discovery fields and add aggregate_id,
universe:{contract_version,route_count,route_set_sha256,complete},
segments:{segment_size,total,pending,running,complete,failed,captured_routes,failed_routes},
and coverage_complete. Coverage becomes complete only when
the exact expected segments all succeed and their route total equals
the sealed universe. Dashboard clients accept retained segment sizes
from 1 through the 200-route capture ceiling so an older resumable
journey remains operable after capture geometry changes. While discovery
is pending, universe
is absent; the API never emits an unversioned placeholder. Resume
replaces only failed segment attempts. The top-level
route_coverage object remains absent until route
reconciliation produces its fixed coverage fields.
reference_capture.started_at records capture creation and
worker_job_started says whether the worker accepted it.
reference_capture.updated_at is the latest bounded status
check for the discovery capture; aggregate polling does not overwrite
it, and clients must not describe it as a worker heartbeat.
Pending, incomplete, and failed evidence is reported with
MIGRATION_SHOPIFY_ROUTE_UNIVERSE_PENDING,
MIGRATION_SHOPIFY_ROUTE_UNIVERSE_INCOMPLETE,
MIGRATION_SHOPIFY_REFERENCE_SEGMENTS_PENDING, or
MIGRATION_SHOPIFY_REFERENCE_SEGMENTS_FAILED. The editable
Preview remains pending until every declared route segment succeeds.
Commerce evidence, parity, and launch also require every declared
adapter to succeed. Retained WooCommerce
journeys cannot advance or resume. MCP exposes
start_storefront_migration,
list_storefront_migrations,
get_storefront_migration,
resume_storefront_migration, and
cancel_storefront_migration. Action completion and delete
remain REST-only. A direct clone deletion cannot remove reference
evidence while its journey remains active. These merchant routes do
not perform DNS cutover or rollback.
The semantic /diag storefront-migration check reports
running and failed reference captures plus authorized imports still
waiting for execution admission, alongside journey status counts.
shopify_preview_adapter_ready counts exact adapter
snapshots that can admit only an incomplete Preview, and
shopify_template_previews_deployed counts those Previews
with a retained deployment receipt. Neither count means launch-ready.
The broad Admin OAuth credential never passes through a journey
response, migration worker, or Wok. A loopback-only internal relay
verifies the exact organization, journey, execution, pinned
connection ID and authorization digest, fixed theme operation, live
lease, and fencing token before it runs the allowlisted Admin query.
The control plane rotates the expiring offline token under encrypted
storage. A second broker binds the managed Storefront runtime to the exact
candidate_deploy fence. The internal Preview deploy route
accepts only the signed gzip and canonical secret-free lineage for
the current compile receipt, injects the sealed provider configuration
itself, and validates the archive into a detached Wok Git worktree.
It commits the exact editable storefront source, deploys an export of
that commit, then fast-forwards canonical Wok Git only after runtime
activation. The immutable receipt keeps separate compiler-package,
Git-export, Wok-commit, and live-runtime digests. Visual definition and
asset edits remain deployable while the generated server and commerce
provider are protected. Failure restores the prior runtime,
environment, and only the managed Git paths, preserving unrelated Wok
edits. A successor execution can replace a prior generated storefront
only when the same journey's active deployment receipt and exact Wok
Git commit prove that no managed-path edit has occurred. It never
changes the live Shopify theme or hostname.
While the journey is active, direct frontend/app-server writes,
edge-function changes, generic promotion, and custom-domain attach or
verification return
CP_STOREFRONT_MIGRATION_MUTATION_BLOCKED. Wok Git is the
editable path because it runs the post-edit commerce gate. The lock
ends when the journey is live, rolled back, cancelled, or failed.
After that handoff, TellWang excludes
runtime-package-manifest.json from deployment and injects
the current platform-owned editable server. Presentation edits do not
regenerate or re-sign the migration package; the commerce provider
remains checksum-protected.
The admitted Shopify workflow imports the published
MAIN theme, preserves its authorized DOM, classes,
responsive CSS, passive assets, and reviewed JavaScript, and binds
commerce to Shopify's APIs. Exact local scripts execute under
checksum-bound signed review and a fixed same-origin browser policy;
modules, workers, inline handlers, embedded external URLs, and
unreviewed bytes block compilation. The browser policy blocks
cross-origin requests from computed URLs. The fixed worker then captures the exact
Preview under the same route universe. Its pinned gate compares
desktop/mobile pixels, DOM landmarks, document height, scroll reach
and effects, interactions, console errors, and network failures.
Commerce receipts are required for product view, variant selection,
cart add/update/remove, search, Customer Accounts, Shopify-hosted
checkout handoff, and every discovered app or unknown dependency.
Missing, duplicate, stale, wrong-deployment, malformed, or failed
evidence blocks the report.
The source Shopify Online Store remains live while the migrated Wok runs at its Preview hostname. No current endpoint publishes or replaces a Shopify theme, changes DNS, or moves shopper traffic. Whole-host activation and rollback are separate future operations. WooCommerce live migration is also future work; dormant verifier and connection foundations are not consumed by the committed journey. See the Storefront Import guide.
Use capture_profile:"quick" for a fixed two-route browser
evidence sample: the canonical homepage plus one same-origin product
detail page selected from bounded sitemap discovery or homepage links.
The source must be the storefront homepage and
max_pages is always 2. If no public product route is
discoverable, the capture fails with
CP_CLONE_CAPTURE_PRODUCT_ROUTE_NOT_FOUND instead of using
an unrelated second page. The receipt exposes
capture_profile:"quick" and
route_selection:"home_and_product". This mode does not
require a Wok, authorize a provider, connect commerce, generate pages,
deploy, or change the source store.
Use capture_profile:"migration" when the rebuild needs
browser evidence beyond HTML and text. The configured
Crawlee/Playwright worker retains up to 20,000 bounded same-origin
sitemap and anchor discoveries, then records up to 200 selected routes
in either manifest contract (100 by default). Sitemap leaf
groups are selected round-robin before anchors fill unused capacity.
The manifest reports discovered, selected, captured, and failed counts
separately, plus rendered DOM evidence,
desktop/mobile screenshots, network observations, scroll traces,
bounded non-navigating interaction outcomes, and live dependencies.
Every bounded interaction, scroll-effect, network, and live-dependency
stream includes observed/retained counts plus a completeness receipt;
an incomplete receipt blocks parity instead of hiding truncation.
Crawlee completes first, then the Browsertrix sibling receives the
exact canonical final route URLs Crawlee captured with page-only
scope. Its fixed driver permits only GET,
HEAD, and OPTIONS, blocks WebSocket requests,
and disables service workers. The sibling rejects missing or extra
page records. The manifest binds the sorted route set with
oracle.route_set_sha256,
oracle.captured_routes, and
oracle.read_only_policy:"safe-http-methods-no-websocket-v1".
The Browsertrix WACZ is required. The candidate sibling is authenticated,
durable, separately built from upstream version 1.12.4, and pinned to
an image digest. Before a remote run starts, the worker fsyncs a
credential-free cleanup marker. Startup and periodic reconciliation
retain that marker until the sibling confirms deletion; worker
diagnostics expose any pending cleanup. Missing, failed,
route-mismatched, or policy-mismatched evidence fails the capture.
A connected password-protected Shopify development store can carry
only TellWang's exact-host temporary storefront session. The worker
seals it before durable job storage and Browsertrix receives it
through a mode-0600 ephemeral file, never a command argument. Because
Browsertrix records request cookies for replay fidelity, the
short-lived session may appear inside tenant-private WACZ evidence
until retention deletes it; it is absent from public receipts and
generated storefront code.
This is bounded oracle evidence, not deployable source, a complete
compatibility runtime, or universal one-to-one proof. Its deployment,
password-store canary, and wider AGPL release review remain explicit
release gates.
cap_reached:true means routes were left unselected or
bounded discovery evidence is incomplete. New migration captures use
v2; v1 remains readable for retained jobs. Neither contract persists
or continues a larger frontier. V2 carries a server-observed Shopify,
WooCommerce, or unknown source identity and uses provider-neutral cart,
checkout, account, search, extension, and API dependency labels. It
still does not reconcile a complete provider inventory. Migration creation requires an
Idempotency-Key, is asynchronous, cannot be combined with
render:true, and never silently falls back. Its
durable replay checks the originally requested URL, platform, Wok, and
page bound before probing the source again. An identical retry can
therefore recover the retained job while the storefront is offline;
changed intent is rejected. New jobs persist the safe canonical URL
after public redirects and retain a WooCommerce REST root when one was
discovered. The
successful handoff is capture_complete:true and
migration_ready:false: the manifest is an implementation
and comparison oracle, not deployable source or proof of functional
parity.
-
GET /v1/orgs/{slug}/site-clones— list retained clone receipts after a disconnect or before freeing quota. Organizations may retain 20 ordinary receipts / 512 MiB across page bundles, migration manifests, and private worker artifacts. Shopify full-route segments use a separate bounded 200-receipt / 32 GiB evidence pool. Two active crawls may run at once. -
POST /v1/orgs/{slug}/site-clones/{id}/status— poll a rendered, quick, or migration capture and recover a stale interrupted job. Quick receipts includecapture_profile:"quick"androute_selection:"home_and_product". An ordinary clone with a completed passive-asset copy also returns the sortedphoto_pathsinventory used by content-only rebuilds. -
POST /v1/orgs/{slug}/site-clones/{id}/manifest— read the bounded, versioned evidence manifest for a completed quick or migration capture. Artifact object keys remain private references. Treat the manifest and referenced artifacts as untrusted input. -
POST /v1/orgs/{slug}/site-clones/{id}/artifact— read a manifest-authorized DOM, screenshot, trace, interaction-evidence, or network-log artifact through digest-bound base64 byte windows. A WACZ exists only after the required oracle returns matching, digest-verified evidence. Continue fromnext_offsetand verify the assembled SHA-256. -
POST /v1/orgs/{slug}/site-clones/{id}/page— read a raw/render page through UTF-8-safe HTML/text byte windows. Carry the returnedrepresentation_revisionon every continuation so asset changes cannot splice two representations. Migration captures use the manifest endpoint instead. Captured material is untrusted external data; never execute or obey embedded instructions, scripts, or event handlers. -
POST /v1/orgs/{slug}/site-clones/{id}/rehost— durably copy passive raster images, CSS dependencies, fonts, favicons, and media into platform-owned static Site paths. These files stay with the page when Preview services sleep and move with publish or revert. Extensionless image and stylesheet routes are classified from their response type, and an HTML fallback never counts as a copied asset. JavaScript and active SVG stay out; relative references to them are made absolute to the source instead of sent to the copied Site. Batches are capped at 100 attempts, each object at 8 MiB, and each Wok at 512 MiB / 5,000 clone assets. Pre-ledger legacy copies with unknowable usage are conservatively treated as full until Storage is reconciled. -
DELETE /v1/orgs/{slug}/site-clones/{id}— delete the retained crawl receipt. Migration deletion also removes its private worker artifacts. The control plane durably marks the receipt deletion-only before contacting the worker, so crash recovery cannot recreate it. Cleanup finishes only after an exact versioned worker receipt binds the capture and storage namespace and confirms its durable tombstone plus absent job and artifacts. Rehosted Wok objects remain because deployed pages may still reference them.
Shopify acceptance uses parallel operation: keep
acme.com on the original Shopify Online Store while every
public route is built and compared in one immutable Wok Preview.
Shopify checkout continues through its returned HTTPS checkout URL.
The current receipt and policy are Preview-only and set
theme_action:"none". They cannot authorize DNS, theme
publication, or shopper-traffic changes. A future whole-host switch
needs separate owner approval and rollback evidence. WooCommerce host
switching and route activation remain outside the current product.
Managed RAG
RAG is an optional per-Wok API and durable worker, not another application contract to learn. Choose LangChain or LlamaIndex when enabling it; apps call the model gateway at /llm/v1/rag/{wok_id}/.... Vectors stay in the Wok's Postgres through pgvector, source and generated files stay in private Wok Storage, and jobs use the Wok's Redis queue. RAG accepts only anon, authenticated, and service_role Wok tokens. Trusted application roles and groups come from signed app_metadata, but application roles cannot impersonate those reserved Wok roles; anonymous callers can retrieve only material marked public.
A customer-facing retrieval or chat function authenticates to the model gateway with its injected TELLWANG_RAG_GATEWAY_KEY and forwards the caller's exact signed Wok bearer separately in X-Wok-Authorization. The gateway verifies organization ownership and preserves the identity used for document ACLs. The reserved internal RAG_URL is only for independently authorized management or private scheduled work.
External messaging providers are application integrations, not TellWang primitives. A WeChat bot, for example, is built from ordinary editable resources inside a Wok: Edge Functions for callbacks and messages, Wok secrets for provider credentials, tables for application state, and the managed RAG API for grounded answers. The reference Wok acknowledges the provider webhook before longer grounded work continues as an Edge Function background task; durable event and delivery rows support recovery. Each visitor has a continuing, organization-visible service thread with bot and human attribution. Questions without a grounded answer enter the shared service inbox, where a Wok administrator can answer directly and separately approve a non-sensitive question and answer for later public RAG. Outgoing model and administrator replies are converted from Markdown to readable WeChat text before delivery. It may be installed as an immutable marketplace Wok release, but installation does not add provider-specific control-plane endpoints, MCP tools, sidecars, or privileged credential paths.
Owners can drive these endpoints through a connected MCP client or Runtime v3 Wang. The dashboard's Enterprise RAG capability generates an app-scoped prompt that covers sidecar setup, governed collections, ingestion, document policy, grounded agents, generation, and insights. The app's Documents surface shows storage buckets, collections, each active indexed file, its governed metadata, and a signed action for the private source. Its Add documents action uploads approved files into a selected collection, requires an explicit public/internal/confidential/restricted access level, and shows parsing and indexing progress before reporting that the files are ready. The org-level Wang knowledge API is different: it holds a small plain-text memory for Wang itself, while app RAG is the governed vector corpus for application features.
PUT /v1/woks/{id}/ragwith{"framework":"llamaindex","profile":"standard"}— enable and wait for readiness. Framework may bellamaindexorlangchain; profile may bestandardorhigh-throughput.POST /v1/woks/{id}/rag/collections— create a collection with a stable Wok-unique slug and baseline policy. The slug can be fixed before execution for exact Wang evidence; the generated UUID remains the data-plane reference. Exact retries are idempotent, while reusing the slug for different settings is a conflict. Documents can allow signed principals, roles, or groups; deny named principals; set public/internal/confidential/restricted sensitivity; or setexcluded:true.GET /v1/woks/{id}/rag/collectionsandGET /v1/woks/{id}/rag/collections/{collection_id}/documents— inspect active revisions, document/chunk/visual counts, source versions, effective access policies, governed metadata, status, and errors with bounded cursor pagination.GET /v1/woks/{id}/rag/collections/{collection_id}/documents/{document_id}/accessmints a 15-minute signed URL for the active private source.POST /v1/woks/{id}/rag/collections/{collection_id}/documents/{document_id}/exclude— quarantine an indexed document without downloading it again. The completed job atomically activates an excluded zero-chunk revision and purges its prior RAG text and vectors; its private source file remains in Storage until separately deleted.POST /v1/woks/{id}/rag/ingestions— submit up to 1,000 existing Storage objects per atomic batch. Each object can carry up to 50 safe flat metadata keys and 16 KiB of scalar or non-empty scalar-list values for department, jurisdiction, document type, version, effective date, or tags. Metadata is versioned with the document and retrieval filters use exact containment after ACL enforcement. Successful later batches extend or update the active collection, so 1,000 is not a collection-size cap. The prior index remains live until the complete batch succeeds. Promotion retains one active membership snapshot plus compact revision metadata, while a failed batch discards candidate-only vectors. Excluded objects are never downloaded or embedded; excluded text ranges are removed before chunking. Promoting anexcluded:trueobject purges its older RAG text/vector revisions; delete the original Storage object separately if the source file must also be erased.POST /v1/woks/{id}/rag/agents— create a grounded customer-service agent with an optional structuredsouldefining its position, organization, mission, tone, conversational style, responsibilities, and boundaries.excluded_document_idscan block up to 500 active files for this agent only while keeping them available to other agents, generation, and insights. End-user chat uses the two-header model-gateway route at/llm/v1/rag/{wok_id}/agents/{slug}/chat. The visitor's exactquestionremains authoritative. Applications may send up to eight priormessageswith avisitor,assistant, orhumanrole. History helps with intent and continuity but is never evidence. Ingestion stores page-aware parent sections and bounded labels found in the source. Same-script questions search the exact contextual wording plus up to two matching indexed labels; cross-script questions keep the exact query and add one model-planned multilingual query. Dense, full-text, and trigram rankings are combined with reciprocal-rank fusion, then at most eight parent-aware passages reach the answer tool. Source labels help search but are not evidence. The answer model may request one bounded retry when no model rewrite was already spent. It treats a standalone topic label as a request to explain it and calls a strict tool with plain answer text, request-local source IDs, and the exact caller-specific scope that still needs a colleague. TellWang constructs citations itself, caps this channel answer call at 256 output tokens and 800 answer characters, and retries one malformed answer call. A supported general answer is no longer discarded merely because a Toronto-specific plan, account detail, or other distinct part needs human confirmation; the response returnsneeds_human_followupandhuman_followup_scopefor the application. A separate strict verifier checks evidence, directness, source actors and certainty, represented-organization identity, and conversation continuity. A rejected draft receives one bounded repair and independent recheck. No domain-specific fallback substitutes a nearby fact for a missing answer. Visual evidence is prioritized only when requested. Anonymous chat is off unlessallow_anonymous:true, and even then only public passages are eligible.GET /v1/woks/{id}/rag/agents— list enabled agents with their exact corpora, excluded file IDs and stable paths, grounding policy, exposure, instructions, and escalation settings.GET /v1/woks/{id}/rag/agents/{slug}— inspect the enabled agent's collections, excluded file IDs/paths, and exact grounding, citation, anonymous-access, instructions, and escalation settings. Optional expected fields let an agent verify the collection set, source-path block list, and exposure setting.DELETE /v1/woks/{id}/rag/agents/{slug}— turn an agent off immediately. Re-saving the same slug re-enables it.POST /v1/woks/{id}/rag/generations— create Markdown, DOCX, PDF, CSV, or JSON drafts from an explicit non-empty document set. Every named file must exist and be readable, otherwise no partial draft is produced. Drafts stay under privaterag-results/generated/, inherit the strictest common source access, and default to a 50-call model budget (maximum 200). Direct app callers need signedrag_authorrole orrag-authorsgroup.POST /v1/woks/{id}/rag/insights— queue summary, comparison, classification, extraction, clustering, or trend work. Optional JSON Schema output must validate before success. A caller-selectedmax_model_callsis capped at 500 to prevent runaway corpus-wide spend; direct app callers need RAG author access.POST /v1/woks/{id}/rag/interview-projects— create a durable, industry-neutral Interview Processing project with a strict extraction schema, governed collections, and confidence threshold.POST /v1/woks/{id}/rag/interview-projects/{project_id}/process— process up to 100 selected interview documents as separate durable jobs. Every populated field needs an exact source quote; weak or incomplete evidence enters review instead of a report.GET /v1/woks/{id}/rag/interview-projects/{project_id}/recordsandPATCH /v1/woks/{id}/rag/interview-projects/{project_id}/records/{record_id}— inspect evidence and confidence, then approve, reject, or correct records against the same schema. Reviewer decisions are audited.POST /v1/woks/{id}/rag/interview-projects/{project_id}/reports— generate a private DOCX, PDF, or XLSX from approved records only. Counts and missingness are deterministic; qualitative findings cite approved record IDs and pass another support check.GET /v1/woks/{id}/rag/jobs/{job_id}— poll asynchronous work. For ingestion, only parent-job success means the new collection revision is active.GET /v1/woks/{id}/rag/jobs/by-key?kind=…&idempotency_key=…— resolve a parent job from the immutable key chosen before execution so Runtime v3 or another connected agent can verify terminal success without depending on a generated job ID.GET /v1/woks/{id}/rag/jobs/{job_id}/items— inspect cursor-paginated per-file outcomes and errors for a large ingestion or Interview Processing batch. Successful PDF items report indexedvisualsand skippedvisual_failurescounts.GET /v1/woks/{id}/rag/artifacts— list generated files and insight outputs durably with provenance and inherited policy.GET /v1/woks/{id}/rag/artifacts/{artifact_id}— policy-check and mint a 15-minute signed URL for a generated file.
Use Wok Storage's resumable upload flow before the manifest endpoint for very large corpora. The multipart /rag/uploads convenience route is capped at 100 files; MCP clients should keep each sequential browser-originated batch below 900 MiB so multipart overhead stays within the management proxy ceiling. Files use digest-bound content-addressed paths. A non-ASCII filename remains the visible title but receives a deterministic ASCII Storage key. Identical retries do not upload again and changed bytes cannot silently replace indexed content; the manifest route remains the scalable contract. Hybrid retrieval combines dense, full-text, and multilingual trigram rankings with reciprocal-rank fusion, then removes candidates below the absolute semantic and text gates. Meaningful embedded PDF pictures and charts are privately vision-described and indexed; one broken image stream is skipped without losing readable text, and the child result reports that failure. Standard Woks describe at most four pictures per PDF and high-throughput Woks at most eight. Those calls use the organization's included model allowance first, then prepaid credits while postpaid usage billing is disabled; model 429/5xx responses receive bounded worker retries. An authorized visual hit carries its page, dimensions, media type, and a 15-minute signed preview suitable for an application channel's media upload. The Documents surface shows a Load more action instead of silently truncating a large collection. Disabling RAG closes its route and revokes its dedicated model key while keeping the corpus in the Wok for re-enable.
Marketplace Wok releases
A marketplace listing is a signed, immutable Wok release, not a prompt or Wang recipe. Installation applies its exact migrations, function module trees, frontend, portable Storage and Realtime declarations, managed RAG setup, and named configuration to an untouched Preview Wok. The buyer owns the result; no model participates and no later version installs automatically. The WeChat reference Wok keeps a continuing service transcript, hides internal citation labels, presents localized source names, answers every grounded part immediately, and places only the unsupported scope into its shared service inbox.
Storefront migration templates use a separate closed contract and never enter the ordinary Wok-release installer. The dedicated registry validates canonical MigrationTemplate v1 manifests, retains the manifest only, and moderates domain-separated Ed25519 signatures through the existing Marketplace keyring. Shopify and Shopline each have an exact operator-trusted workflow, dependency-only compatibility WokRelease, parity profile, and connection contract. Their identities and immutable digests are not interchangeable. Publication and every later read re-verify the complete provider-specific manifest; WooCommerce remains parseable future work but cannot publish.
Marketplace is a top-level dashboard destination beside Feed and Apps. The plus control in Feed and Apps, and the New app control on the Apps page, open the same choice: start an app build in chat or browse ready-made Woks in Marketplace.
GET /v1/marketplace/catalog— public safe metadata for the newest published version of every release. Its customer-facing category supplies the Marketplace industry filters; technical capabilities remain separate.GET /v1/marketplace/releases/{release_slug}?version=1.4.1— public release detail with compatibility, file checksums, signing-key identity, and configuration declarations. It reads the stored validated manifest without opening the archive, and never returns package bytes or configuration values.GET /v1/orgs/{slug}/marketplace/migration-templates— authenticated catalog containing only exact signature-verified, operator-trusted Shopify and Shopline manifests. Any published record outside that registry makes the read fail closed. MCP tool:list_migration_templates.GET /v1/orgs/{slug}/marketplace/migration-templates/{template_slug}?version=<version>— trusted signed manifest detail. Shopify and Shopline publish independent immutable versions; the current Shopline template is1.0.2. A published template returnsstart_available:trueandstart_path:"/orgs/{slug}/storefront-migration-journeys". WooCommerce remains parseable future work but is rejected by trusted publication and catalog verification. MCP tool:get_migration_template.POST /v1/marketplace/migration-templatesplus the bootstrap-onlyapprove,publish, andrejectroutes — operator moderation for the dedicated manifest registry. Publish verifies the stored digest, signature/key identity, and complete provider-specific trust registry.POST /v1/orgs/{slug}/marketplace/installations— start an exact installation with anIdempotency-Key. Body:{"release_slug":"wechat-rag-bot","release_version":"1.4.7","wok_id":"…stg","configuration":{"NAME":"value"}}. The target must be the caller's linked Preview and may contain only TellWang's byte-exact untouched starter; live public database relations also count as work. Installs are serialized per Wok, heartbeat while active, and expose a failed receipt with safe detail. A crash-orphaned receipt can retry only while the Preview remains empty.GET /v1/orgs/{slug}/marketplace/installations/{installation_id}— read the durable install, integrity, deployment, and verification receipt.
An MCP-native agent reaches the same endpoints through list_marketplace_releases, get_marketplace_release, install_marketplace_release, and get_marketplace_installation. The install tool takes an optional idempotency_key and mints one per call when it is omitted; pass the same key to make a retried call replay one attempt instead of starting another. Gates live in the endpoint, not the tool: a production target, a Preview holding customer work, an unsigned or altered artifact, configuration names that do not match the signed declaration, and blank required values are all refused. build_skill generates an application from instructions and is not a route to a marketplace release.
Moving an installed app to a newer version
Installation needs an empty Preview Wok. Once you have edited the app you own, that requirement is exactly wrong, so upgrades are a separate operation with a three-way merge: the version you installed, the newer version, and your Wok as it is now. A file you never touched follows the release. A file the release did not change stays as you left it. A file both changed since installation stops the upgrade — nothing is written, and the receipt names every such path for you to answer.
GET /v1/orgs/{slug}/marketplace/upgrades/plan?wok_id=&release_slug=&target_version=— the merge, computed and returned without touching the Wok. Shows which files follow the release, which stay yours, which migrations are new, and every conflict.POST /v1/orgs/{slug}/marketplace/upgrades— apply it, with anIdempotency-Key. Body:{"release_slug":"…","target_version":"1.1.0","wok_id":"…stg","configuration":{},"resolutions":{}}. Conflicts return409 CP_WOK_RELEASE_UPGRADE_CONFLICTand aconflictedreceipt; retry withresolutionsmapping each path toupstreamorcustomer.GET /v1/orgs/{slug}/marketplace/upgrades/{upgrade_id}— the durable receipt:applying,applied,conflicted, orfailed.
Databases move forward only. A newer version may add migrations; it may not rewrite one your Wok already ran, because that statement's effects are already in your data. That refusal is not something a resolution can override — it belongs in the release. The same holds for a changed managed-RAG declaration, which needs a fresh Preview/Published pair for the same reason promotion refuses it. A clean upgrade re-runs the release's verification contract against the merged app, then records the new version as your Wok's base. Published is untouched until you promote, and promotion applies its own gates again. The MCP tools are plan_marketplace_upgrade, upgrade_marketplace_release, and get_marketplace_upgrade.
After Preview verification, ordinary Wok promotion replays the signed release's portable bucket policies and managed-RAG declarations onto Published without copying staging rows or Storage objects. Anonymous bucket writes are create-only. A newly introduced RAG runtime and its model key are retired if a later promotion gate fails; a different RAG release version refuses to overwrite an existing runtime until resource rollback is available. Provider-specific behavior remains editable Wok code.
Audit log
Every state-changing call to TellWang 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 Feed reads the org activity stream, while this per-Wok endpoint remains available to any authorized REST or MCP client.
Web analytics & session replay
Apps with first-party analytics enabled report cookieless pageviews, daily visitors, custom events, sources, campaigns, country/region, device class, browser language, privacy-safe clicked elements, conversion outcomes, aggregate click cells, and scroll depth. GET /v1/woks/{id}/analytics/web?days=7 returns traffic plus those aggregate breakdowns, including countries for all visitors and countries for visitors who clicked. Each demographic bucket counts distinct daily visitors, not cross-day identities. GET /v1/woks/{id}/analytics/heatmap?path=/&days=7 returns a normalized 20×30 click grid and ten scroll-depth buckets.
Caddy validates direct and Cloudflare-proxied client addresses before country and region are derived against a local database, then the raw IP is discarded. Referrers are reduced to an external hostname. Add data-tw-analytics="pricing-cta" to name an interactive element without sending its text or selector, and call window.twConversion("signup", "success") with an outcome of success, failure, or cancelled. Raw click coordinates, selectors, DOM text, input values, and full referrer URLs do not enter these aggregate datasets. Age and gender are not inferred or stored. IP geolocation by DB-IP.
TellWang's platform-operator-only Live Wok Data Room lists every active production Wok, with or without a connected domain. It shows cumulative Wok-scoped Wang/Agent Cell execution time and tokens, the latest run, optional domains, and the same aggregate audience rollups. Queue time and customer-facing app LLM calls are excluded from build usage. Every cross-customer list or detail read is operator-gated and audit-logged. Analytics is Wok-level, so platform traffic counts each Wok once even when several domains route to it. It does not expose raw collection files, raw IP, age, gender, person profiles, or session replay.
Session replay is separate and off by default for each Wok. An owner or admin can enable it in the Analytics tab or with PATCH /v1/woks/{id}/analytics/session-replay and {"enabled":true,"capture_visible_text":true,"acknowledge_privacy":true}. Existing Woks default to masked text. Full-fidelity mode records ordinary visible page copy, layout attributes, and web fonts; form inputs remain masked, data-tw-mask protects selected text, data-tw-block excludes a sensitive subtree, and canvas stays off. The response separates the saved Wok preference from recording_available; TellWang production records every eligible human session after that opt-in. Global Privacy Control and automated browsers stop the recorder, while the retired Do Not Track signal does not. Private lifecycle markers account for idle reading time even when the visitor does not click or type. Each upload is signed for one Wok, one unlinkable session, and the selected privacy mode. Public bootstrap and ingest are rate-limited; ingest redacts input values, event handlers, and URL queries/fragments again before owner-only storage capped per session, per Wok, and globally.
GET /v1/woks/{id}/analytics/sessions?since_hours=24&limit=100&cursor=...— cursor-paginated recording summaries after a server-side time filter. Rows include entry URL, duration, viewport, event count, completeness, client-error co-occurrence, and rage/dead-click signals. Requires the build capability.GET /v1/woks/{id}/analytics/sessions/{sid}— rrweb events for the dashboard player plus the recording'scapture_visible_textpolicy. Requires the build capability.PATCH /v1/woks/{id}/analytics/session-replay— enable or disable recording and selectcapture_visible_text. Owner/admin only; enabling requires the privacy acknowledgement.
Wang uses list_session_replays to triage these summaries without reading raw DOM events, and configure_session_replay after an owner explicitly accepts the privacy behavior. The control plane enforces seven-day cleanup itself rather than relying only on a host timer. Verified serving custom domains are reconciled at startup to the same complete collection and recorder-library route set as standard Wok hosts.
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. Use REST directly or connect an MCP client from Build with an agent.
Frontend changes ship through git
Every plan gets the Wok's own git repository. git_snapshot returns a clone URL for the Preview Wok; edit the checkout with your normal tools, commit, and push — the push deploys frontend/ and functions/ through the authenticated release path and returns a receipt. Git pushes and browser_check now hold a Preview activity lease while they run and refresh the idle clock before work begins. The staging autosleep pass checks that lease and clock again immediately before stopping containers, so it cannot take an actively deployed or tested Preview offline from a stale hourly snapshot. An active Shopify migration can be snapshotted after Import has installed its committed editable-source baseline. That baseline keeps a small storefront-source/manifest.json and one HTML file per captured route; TellWang rebuilds the large runtime definition only inside the deployment artifact. The snapshot is serialized with migration work, preserves the managed storefront source and handoff files, and ends the mechanical migration journey. A shopify_migration_handoff: true receipt means the snapshot is now the owner-managed baseline and later pushes behave like those of an ordinary Wok instead of waiting for migration candidate evidence. Verify with browser_check, then promote with the receipt's pins. The older in-place tools (deploy_frontend, read_frontend_file, edit_frontend_file) are retired from the agent surface; the raw POST /v1/woks/{id}/frontend/read and /frontend/edit endpoints remain for dashboard and service integrations.
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":"<index.ts>","files":{"lib/helper.ts":"<source>"}}.filesis optional and contains local modules relative to the function directory; the complete tree is capped at 256 KiB. Name^[a-z][a-z0-9-]{0,30}$. On public invocation, the shared edge overwritesX-TellWang-Client-IPwith the validated end-user address before the function runs. Cloudflare forwarding headers are accepted only from Cloudflare's published networks; direct and DNS-only domains fall back to the socket peer. Use that private header for abuse controls instead of trusting caller-supplied forwarding headers. 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, module_count, invoke_url, created_at, updated_at}(omits source bodies).GET /v1/woks/{id}/functions/{name}— returnscodeplus the localfilesmap so you can pull the complete deployed function.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, JWT_SECRET (the stack's JWT signing secret — the same one GoTrue signs with and PostgREST validates against), SUPABASE_DB_URL, REDIS_URL (per-wok Redis 7 container, AUTH via the wok's db password). These are injected and platform-managed — set_env rejects them; read them with Deno.env.get(...). The Redis instance is bullmq-ready out of the box — see Functions → Redis + BullMQ for the canonical pattern.
Release receipt
GET /v1/woks/{id}/release answers what is live without reconstructing it from several tools. It returns the newest attempt, the current successful release, the previous rollback target, the Wok Git source commit, the shared release manifest, a SHA-256 digest of the deployed bytes, changed runtime surfaces, verification evidence, and publish/rollback approval identity. A newly activated deployment remains verifying; it cannot become succeeded until every required probe passes. The current release follows runtime activation order, so a delayed probe cannot make an older candidate replace newer live bytes in the receipt.
release_status=verified means the exact frontend tree, complete function-module tree, ordered tenant migration checksums, Wok Git source, and runtime artifact all match the manifest, and every effective changed surface has relevant passing evidence. Frontend/SSR changes need a browser or public-URL check, function changes need an invocation, and database changes need their declared postconditions. A normalized byte-identical frontend receipt is marked effective_noop:true and finishes without browser evidence only when an earlier non-no-op receipt already proved those exact live bytes. An unmarked empty surface list does not silently pass, and a no-op receipt cannot bless or supersede a failed verification. Promotion still compares Published with the full pinned Preview manifest, while requiring new evidence only for effective Published changes. A newer failed attempt is never skipped. External remote URLs are credential-scrubbed and compared with their last fetched tracking ref; Wok Git remains authoritative. A release whose required check never ran is recorded as unverified rather than failed: the content was deliberately retained and is still live, nothing reported a problem with it, and it can still accept the check it is waiting for. Browser checks require a completed navigation, visible rendered content, and every requested assertion to pass. Uncaught exceptions and console.error output stay visible for diagnosis, but inherited analytics or legacy-script errors are warnings when the page still renders and every target assertion passes; they no longer auto-revert an unrelated honest change. A blank render or failed target assertion still fails and compensates the changed surface. Use element_attribute to verify exact DOM attributes such as a meta description, and request no_console_errors only when a clean console is itself part of the change. A page that never loaded, and a check run with no assertions, both record no evidence in either direction — they observed nothing. Reading the receipt never waits out a release in progress: while a deploy or promotion still holds the Wok's release lock, the receipt returns release_in_progress:true and reports the alignment verdicts as unavailable rather than comparing a tree that is mid-write. The deployment history and statuses stay current; poll again once the release settles.
POST /v1/woks/{id}/releases/{deployment_id}/rollback creates a new audited rollback release from a source-linked successful deployment. It restores frontend and functions exactly, including removing functions absent from the target, and disables any enabled function schedule whose target disappeared. It never deletes history or reverses schema/data. Before mutation TellWang persists the current source and complete function-schedule set; a failed rollback replays both so partial code and dangling timers are not left live.
Publishing queues a server-owned job bound to the exact newest verified Preview deployment, its artifact digest and its full manifest. The call returns a deployment id immediately; clients poll that receipt, and disconnecting does not cancel the release. HA replicas periodically adopt abandoned queued or building jobs under a database lock. The worker reconstructs code from the pinned Wok Git commit, preserves generated SEO files as exact snapshot bytes, reapplies the anon-key and analytics transforms that snapshots intentionally remove, and rechecks live Preview immediately before touching Published. TellWang rejects an older id, a mismatched digest, a newer failed or unfinished attempt, or source, artifact, context or manifest drift. Published cannot verify unless its complete function tree, derived static frontend or source-backed storefront tree, migrations and declared surfaces match the pinned manifest. A terminal Shopify storefront carries its exact managed Git source to Published and activates the canonical SSR runtime after its protected runtime environment is ready; an empty frontend/ directory is not classified as a no-op. Its receipt pins the editable definition, provider and assets independently of the platform-generated server and retired migration attestation. A later failed frontend check restores frontend only; a failed function check restores functions and schedules only. A source-backed Shopify SSR frontend has no static frontend/ tree, so a frontend-only verification failure replays the preceding verified Wok Git source through the validated app-server deploy path and probes the restored origin instead of pretending an empty static-tree copy restored the Node runtime. Mixed frontend/function failures retain the surface-scoped path. Restore targets are preflighted from immutable Git and their live hashes are checked before compensation completes. Explicit no-op receipts never supply independent proof, supersession, or a runtime pin. A failed staging promotion creates a durable production release hold only after Published was actually mutated. TellWang keeps clone/fetch available, but a Git push cannot activate production under the old promotion approval. Repair staging and submit a new pinned release. Calling create-staging first proves the linked Preview's database and every rendered service are reachable. If its Compose runtime or network is missing, TellWang reconstructs the infrastructure from persisted state and verifies that application artifacts did not change before returning success. It also repairs an existing linked Preview without an operator when its application schema was lost: TellWang replays Published's schema only, never production rows, and adopts legacy ledger history only after the two application-schema fingerprints match exactly. A non-empty divergent Preview is preserved, and historical ledger rows remain append-only.
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
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.POST /v1/woks/{id}/function-schedules— body{name, function_name, every_seconds, payload?, enabled?}. TellWang invokes the Edge Function every 60–86,400 seconds with a fresh Wok-scoped service-role credential in the request headers; credentials are never persisted inpayload.GET /v1/woks/{id}/function-schedules— lists run count, last status/error, and next invocation.DELETE /v1/woks/{id}/function-schedules/{name}— removes the recurring invocation without deleting the function.
Environment variables
PUT /v1/woks/{id}/env/{name}— body{value}. Name^[A-Z][A-Z0-9_]{0,63}$. Encrypted at rest under the TellWang's key store (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 → Other providers. Google sign-in needs none of this — enable_google_login uses TellWang's own Google client. GOTRUE_RATE_LIMIT_HEADER is platform-managed: the edge overwrites its matching private header with the validated end-user address before proxying Auth, and a customer value cannot replace that boundary.
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/signupstarts verified signup; it creates no account until the email code and official Twilio Verify phone code both pass. The dashboard also offers a privacy route: choose a Wallet Standard-compatible Solana wallet such as MetaMask or Phantom, sign a one-use message, then send at least $1 USDC by QR code, wallet link, or copied address. The full confirmed amount becomes workspace credit.POST /v1/loginremains password sign-in for existing accounts. All public auth steps are rate-limited, and the optionalinvite_codegate applies to both signup routes.GET /v1/plans— the public catalog for Free, Pro, Scale, Scale + HA, and Enterprise. The response includes display copy, monthly price, app quota, Agent Cell concurrency, runtime defaults, and a sharedusageblock with final model, Functions, SMS, Storage, Email, and domain rates. App limits are 1 / 1 / 5 / 5 / 10; each live app includes its linked Preview. Database standby and redundant REST begin with Scale + HA. Lollipop's public homepage projects the catalog's current numeric plan limits into its pricing chart and does not keep a separate fallback price list.GET /v1/orgs— organizations the signed-in human belongs to, including their role, current organization, and plan.POST /v1/orgs/new— create a named organization and become its owner. Body{name}accepts 1–80 Unicode characters. Your first owned organization is free; another requires ownership of a paid organization, with a 20-organization account cap. Send one stableIdempotency-Keyper logical attempt so a lost response can be retried without a duplicate. The dashboard does this automatically and exposes the action in the desktop workspace menu and as New org on mobile; the mobile Orgs list can reopen an organization when the first switch fails.POST /v1/orgs/{slug}/token— switch a signed-in human into an organization they belong to by minting a fresh organization-bound session key. Call this after/orgs/new; if switching fails after creation succeeded, retry this token request without creating the organization again.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 requested grant is capped at your plan. The REST body and MCPmint_llm_keyschema acceptprivate,deepseek-v4-flash,deepseek-v4-pro, and paid-plankimi-k2.7-code. The current meter consumes the organization-wide included allowance when completions finish, so/topupreportsnot_requiredinstead of duplicating that allowance onto a key.GETlists key usage, included tokens left, and the published token-type rates;DELETErevokes. The old per-key/checkouttoken bundle returns 410CP_LLM_CHECKOUT_RETIRED; usage beyond included allowances uses organization prepaid credits. 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;DELETE /v1/orgs/{slug}/email/domain/{domain}revokes the provider domain before removing the local row, releasing its provider slot without deleting inbox mail. See Email → Bring your own domain.POST /v1/orgs/{slug}/billing/checkout/subscription— buy apro,scale, orscale_hafixed-price subscription through card-only Stripe Checkout. Checkout collects the subscription payment method but omits postpaid Terms consent while postpaid usage billing is disabled. Usage beyond included allowances uses prepaid credits. Canonical Stripe verification and the durable reservation still bind the exact purchased plan safely.POST /v1/orgs/{slug}/billing/checkout/postpaid— temporarily returnsCP_POSTPAID_DISABLED. Add prepaid credits for usage beyond included allowances.GET /v1/orgs/{slug}/billing/subscriptionreportspostpaid_enabled:falseand keeps historical usage-charge states visible.
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. Operators can backfill plan runtime defaults for existing Woks with POST /v1/orgs/{slug}/runtime-defaults.
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.
Payment gateways
A gateway can be created before Stripe is ready, but card collection stays fail-closed until the platform has its Stripe API key plus separate signing secrets for account events and connected-account events.
POST /v1/orgs/{slug}/projects/{project}/payments/kyc— saves the real legal identity for one customer-facing Project and immediately creates that Project's Stripe-managed Connect account. Stripe collects the official KYC, gives the client a full Stripe Dashboard, assumes connected-account payment-loss responsibility, and charges processing fees to that account. TellWang creates card checkouts as direct charges in the Project's Stripe account. The account slug remains only the authorization container. Success returns{project_id, account_id, onboarding_url, expires_at}, which proves Stripe received the submission. The onboarding URL is single-use; open it immediately.POST /v1/orgs/{slug}/projects/{project}/payments/onboarding-link— creates a fresh link for that Project when onboarding was interrupted or the first link expired.GET /v1/orgs/{slug}/projects/{project}/payments/balance— reports only that Project's collected totals pluscharges_enabled,payouts_enabled, anddetails_submitted. Projects in one account do not share payment identity or a connected account.GET|POST /v1/orgs/{slug}/projects/{project}/payment-gateways— lists or creates gateways owned by that Project. A supplied Wok must be linked to the selected Project; unmapped legacy gateways fail closed for card payments.
If submission returns CP_BILLING_NOT_CONFIGURED, TellWang keeps the local profile pending but makes no claim that it reached Stripe. The operator must install every credential named by /v1/diag, then retry the same submission.
Domain registration
Buy a domain through Cloudflare Registrar with TellWang as the trustee. Registration starts at $5 USD; the quote returns the exact registration and renewal prices before purchase. 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 Registrar API supports them.
POST /v1/orgs/{slug}/domains/check— body{names:[], expected_max_price?}. Returns a per-name quote:{ok, name, tier, billed_usd, renewal_price_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}/seo— read Google Search Console status for a domain owned, claimed, or connected to the org.POST /v1/orgs/{slug}/domains/{name}/seo— register the domain and submit its sitemap. Managed DNS verifies automatically. External DNS returnsstatus:"connection_required"with the exact TXT record instead of failing; add it and call again.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.
When connecting an owned domain to a Wok, pass dns_mode:"tellwang" to create or claim a TellWang-managed Cloudflare zone and receive assigned nameservers, or dns_mode:"external" to keep the current DNS provider and receive the ownership TXT plus direct A record. Nameserver delegation changes DNS hosting only; it does not transfer registration. Omitting the mode keeps automatic discovery.
Wang — the customer-facing agent
Wang is the agent layer described on How Wang works. Every current plan includes a hosted Agent Cell. Free includes one concurrent run and DeepSeek Flash; paid plans add DeepSeek Pro, Kimi, and higher limits. Private remains in the technical model gateway but is hidden from Wang chat. Any signed-in member can also connect their own ChatGPT or Grok subscription as a per-person choice.
- In-dashboard Chat — open tellwang.com/dashboard. Each organization gets its own OpenCode Agent Cell, with an isolated human-bound worker for every active thread. The dashboard streams the same durable run and approval requests; it never receives an OpenCode credential.
- Channels — connect a Telegram or Slack bot in the dashboard's Channels tab and your team talks to Wang from the chat app they already use. See Channels below.
Wang chat persistence
The in-dashboard Wang chat keeps the conversation and run state on the server, so logging in from a fresh browser brings the thread back. TellWang relays the organization's OpenCode Agent Cell into this same transcript on every plan, including tool progress and permission or question cards that need your reply. Agent turns use the selected model's context and the checkout's own AGENTS.md and TELLWANG.md; unrelated Wang product recipes are not replayed on every OpenCode round. TellWang interrupts an active turn after 16 inspection calls or four exact repeated inspections without an edit, verification action, or blocker, then gives one checkout-aware correction. If the loop repeats, it retires only that engine context with CP_AGENT_CELL_NO_PROGRESS; the next attempt starts clean with the recent TellWang thread restored, while completed Wok work and the durable transcript remain unchanged.
GET | PUT /v1/orgs/{slug}/wang/model— read or select the plan-aware Wang catalog. Free sees DeepSeek Flash; paid plans also see DeepSeek Pro and Kimi. Account-verified ChatGPT and subscription-backed Grok Build belong to the signed-in member, not the organization, so selecting either personal provider never spends a teammate's account. Private remains accepted for legacy/non-app continuity but is not shown in Wang chat.- App-building model rule — Create app, Edit app, every app-focused chat, and their app-build follow-ups require DeepSeek, Kimi, connected ChatGPT, or connected Grok. The run request may carry the dashboard's closed-vocabulary
capability; TellWang persists it only for model suitability and continuity, never as authorization. A Private app-build submission returnsCP_WANG_APP_MODEL_REQUIREDbefore its prompt or run is stored. Every hosted Agent Cell key is also bound to a private or coding model scope; Private keeps read access but cannot cross the server-side build boundary through MCP, the TellWang CLI, or Git push. POST /v1/orgs/{slug}/wang/providers/chatgpt/connect— begin ChatGPT device sign-in. Follow the returned verification link and enter its short code; pollPOST …/connect/{attempt_id}until connected. Wang checks automatically, explains that completion may take a few seconds to arrive, and offers Check now after the member finishes in ChatGPT. While sign-in is pending, Cancel connection stops polling and invalidates the code.DELETE …/providers/chatgptcancels that pending attempt or removes the encrypted connection and returns the member to the organization model.POST /v1/orgs/{slug}/wang/providers/grok/connect— begin official Grok device sign-in. Follow the returnedaccounts.x.ailink and enter the short code; pollPOST …/connect/{attempt_id}until connected. TellWang checks automatically and offers Check now. While sign-in is pending, Cancel connection stops polling, invalidates the code, and retires the isolated sign-in runtime. The official client's OAuth state is encrypted in the control plane, refreshed in an isolated one-shot container, and never projected into an Agent Cell.DELETE …/providers/grokcancels that pending attempt or removes the encrypted connection.GET /v1/orgs/{slug}/wang/chat— returns the last 200 owner-visible messages oldest-first as{messages:[{id, role, content?, run_id?, display_kind?, has_image?, author_email?, author_name?, tool_calls_json?, tool_call_id?, tool_name?, created_at}]}. Human messages carry their speaker identity in a group conversation. Runtime v3 returns typed reasoning activity, public narration, and the explicitly published final answer; the unpublished final-answer candidate remains hidden. A screenshot-bearing user message hashas_image:true; its image bytes stay out of the transcript response. 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.GET /v1/orgs/{slug}/wang/chat?thread_id={id}— return the last 200 owner-visible messages. Runtime v3 stores OpenCode reasoning asdisplay_kind:"activity"for the collapsed Thinking accordion and public model text asdisplay_kind:"narration"for ordinary assistant messages. The independently accepted final answer is a separate published row. A conversation from before typed streams regains its historical mixed row as legacy activity when no classified activity sibling exists; TellWang keeps that row together instead of guessing an answer split. The unpublished final-answer placeholder remains private.DELETE /v1/orgs/{slug}/wang/chat— clear all messages for the org. Returns 204.GET | POST /v1/orgs/{slug}/wang/threads— list or create organization-level chats. Rows include a participant count and anarchivedflag. Non-empty pre-v3 conversations appear under Archived chats, including early browser-driven v1 transcripts that predate run records; continuing one with Runtime v3 returns it to the current list. No transcript is deleted. A new thread may include an optionalwok_idas its focused starting context.PATCH …/threads/{id}changes or clears that focus; it never limits the chat to one Wok, and every action still authorizes its explicit target.GET | POST | DELETE /v1/orgs/{slug}/wang/threads/{id}/members— manage the chat as a private Slack-style group. Add existing organization members, list the people who can read and reply, remove someone, or leave. Adding someone to a chat never grants organization access.POST /v1/orgs/{slug}/wang/runs— submit a human-attributed Runtime v3 message to the organization's durable Agent Cell queue. Omitruntime_versionor send3. The optional screenshot must be a fully decodable base64 JPEG, PNG, or WebP data URL no larger than 700 KB, 4096 px on either edge, or 2 megapixels; invalid images returnCP_INVALID_BODYinstead of being silently dropped. Image decoding is concurrency-bounded and rate-limited per organization;CP_RATE_LIMITEDincludesRetry-After. Free and Pro allow one concurrent worker; Scale and Scale + HA allow four; Enterprise allows eight. Free includes DeepSeek Flash and 1M model tokens monthly; paid plans add DeepSeek Pro and Kimi. A member may also select connected ChatGPT or Grok; either subscription connection applies only to that member's runs and allowance. Each chat has one active driver, while additional teammate messages queue oldest-first instead of failing when Wang is already working. For Wok-focused work, an exact owned Wok named in the current or most recent thread request becomes that run's focus, maps to Preview, and gets a retained Git checkout before OpenCode starts; the organization chat itself remains unbound. A personal-provider worker advertises only the exact provider model to OpenCode. Switching models replaces that thread's worker and engine context while preserving its checkout and TellWang transcript. The response saysrunningorqueued, includes the queue position or execution start time, and exposesrequires_coding_modelso refreshed clients preserve app-build suitability. Retired versions1/2return 410CP_RUNTIME_RETIRED. OpenCode ending an assistant turn does not automatically finish the run: an independent model judges the chronological user goal against Wang's answer and secret-free tool ledger, and only a high-confidence complete verdict succeeds. Before that judgment, TellWang deterministically continues an action turn whose answer asks routine permission despite having no successful edit, mutation, or verification evidence; genuine blockers must use the durable request mechanism. An explicit request to execute a named tool ortellwangcommand likewise requires at least one successful tool record before model judging, so a pasted command cannot be treated as an observed result. OpenCodereasoningparts stream into the collapsed Thinking accordion, whiletextparts stream as ordinary Wang narration outside it; both remain classified on failed or cancelled runs. Completion-judge prose is never copied into the transcript. TellWang retries one recoverable transport, timeout, HTTP 429/5xx, or malformed-verdict failure; permanent 4xx responses fail immediately. If evaluation remains unavailable, the run fails closed and publishes no unevaluated final answer. Only a high-confidence completion publishes text from OpenCode's exact completed assistant message; TellWang durably records that message id before the event stream stops and clears it before any later prompt. That accepted message is removed from public narration to avoid duplication, its reasoning remains in Thinking, and the immutable publication marker plusstatus=donecommit atomically.GET …/runs?thread_id={thread-id}restores the active stopwatch, completed duration, submitter names, queued messages, and coding-model requirement after refresh;GET …/runs/{id}/imagereturns an attached screenshot only to someone who can open that run's chat, with private, no-store caching, and chat history fetches it only when the reader chooses View screenshot;GET …/runs/{id}/eventsstreams reasoning activity, public narration, the accepted final answer, safe tool targets and truthful tool states, status, queue changes, and approval/question requests;DELETEcancels the active or queued turn.- Managed DNS completion — the secret-free tool ledger includes only revalidated operational labels, never raw results. A managed-zone one-shot verification with a future
next_check_afterderivesexternal_wait:"managed_dns_propagation", even when the domain was connected in an earlier run. After reporting the truthful pending-activation state, Wang callslollipop finish-run; TellWang validates that server-derived state and ends the run without injecting another completion turn. External DNS that still needs customer action and ordinary unfinished work remain incomplete. POST /v1/wang/agent-cell/finish— backing endpoint forlollipop finish-run. It accepts only an authenticated Agent Cell whose active run already has a supported, server-derived terminal wait state. It records the accepted exit for the completion evaluator; an unsupported or ambiguous exit returnsCP_AGENT_CELL_FINISH_UNAVAILABLE.- Thinking boundary — the completion judge's raw response stays in its private evaluation record. If that verdict requires another Agent Cell turn, TellWang writes the bounded evaluator note and continuation instruction as durable
activityfor the collapsed Thinking accordion. OpenCode's user-turn echo is hidden even when its text arrives before its role metadata, so customer prompts and internal harness guidance never become public narration. preview_selectiononPOST /v1/orgs/{slug}/wang/runs— optionally carries version1, the focused or linked Previewwok_id, and 1–20 bounded semantic element targets (tag, optionalid,classes,role,name,type, andselector). TellWang validates the locator grammar and thread/Wok relationship, persists the selection separately from visible chat text and embeddings, and gives it to the Agent Cell and completion judge as distinctly labeled, bounded untrusted data. Invalid selections returnCP_INVALID_BODY.POST /v1/orgs/{slug}/wang/runs/{id}/handoff— accept or decline an offered Kimi continuation. Acceptance sends the work to a third-party model. The successor keeps the source run's validated screenshot and semantic Preview selection in separately stored fields so “the selected element” stays grounded; raw locator data does not appear in the visible continuation message or embeddings.GET | POST | DELETE /v1/orgs/{slug}/agent-cell— read, ensure, or stop the organization's cell on any current catalog plan.GET …/agent-cell/requests?run_id=…lists pending interactions;POST …/requests/{id}/replyanswers them.GET | POST | DELETE /v1/orgs/{slug}/engineering-pilot— private Scale-plan Lavarage pilot lifecycle and dispatcher health. A named owner/admin remains the approval authority, configures the Delivery Ed25519 public key, and may rotate the one-time-displayedepd_key atPOST …/dispatcher-key. That credential is rejected by ordinary TellWang APIs and is accepted only byPOST …/tickets. First machine admission also requires a five-minute, one-use grant signed by the separate Delivery trust root and bound to the exact organization, admitted/guarded issue revision, configured repository and base, spec/planning/graph digests, enabledENGINEERING_ADMITTEDdelivery-admitedge, and durable request digest. Exact replay returns the original ticket after expiry; changed evidence conflicts. Admission is capped at eight open tickets and ten machine admissions per hour.GET | POST …/ticketslists or queues bounded tickets that move through fresh-context Builder, Reviewer, and QA Agent Cell turns before stopping for human review. Only Builder receives the repository-limited GitHub credential and egress. Reviewer and QA receive neither and must emit the exact read-only checkout evidence marker; generic shell calls and failed Git commands do not count. An initial prose-only answer gets one bounded corrective turn, then fails closed. The dispatcher trusts a numbered PR URL only from successful shell evidence, rejects comparison links, and verifies through GitHub that the proposal is open, draft, unmerged, and maps the exact ticket branch to the ticket's pinned base branch.POST …/tickets/{id}/approve|rejectrecords that decision. The pilot never merges, deploys, or receives production, funds, credential, or smart-contract access. An operator installs the repository-limited GitHub credential asengineering-pilot-github-token; it is never returned by this API.
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. Telegram/Slack channels and connected MCP clients can consume this context; the dashboard prompt handoff does not run a model itself.
- 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. - 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.
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, read, received_at}).GET …/inbox/{message_id}— fullbody_text+body_html, attachment metadata, and durable read state.PATCH …/inbox/{message_id}with{read:true|false}updates that state without changing the retained message.GET …/originaldownloads the exact retained.eml;GET …/attachments/{index}downloads one decoded attachment without exposing the account key in a URL.DELETE …/inbox/{message_id}— idempotent.- Wang reaches these through the
list_inbox+read_emailMCP tools.set_email_forwardingadds durable, metered forwarding with readable attachments, an exactoriginal_message.eml, and original SMTP-envelope headers;get_email_forwardingreports retry health andremove_email_forwardingdisables it. GET /v1/woks/{wok_id}/email-handlerreads the initial approved inbound automation, its draft/enabled/paused/failed state, exact message-field permissions, safe-test receipt, and retained test/live history.POSTsaves the deployed function, bounded sender/recipient/subject filters, permitted fields, and lifecycle state.POST …/email-handler/testchecks the exact draft against an owned Inbox message or safe sample without invoking the function or causing an external side effect; Enable requires that unchanged receipt.DELETEremoves the rule without deleting the function, Inbox messages, or prior receipts. Setting, testing, pausing, resuming, and deleting require build permission.GET /v1/woks/{id}/email-forwardingreturns destination, enabled-since time, pending/failed counts, last success, and active delivery rows with recipient, attempt count, retry time, stable error code, and a safe diagnostic. It never returns the message body or raw provider error.GET /v1/email/domainsreturns independent receiving and sending states for each email domain, the required inbound MX, sending DNS records, detected-publication flags, and bounded recovery guidance. Stored provider errors are not exposed.POST /v1/email/sendaccepts optional ownedwok_id, Inboxreply_to_message_id, and Base64attachmentswith filename and optionalcontent_type(or compatibility aliascontentType). The complete encoded message is capped at 40 MB. Attachment bytes remain in the durable provider outbox and idempotency identity until delivery settles. A dashboard reply stores one durable sent copy in the same reservation transaction as its idempotent billing receipt; message detail returns those sent replies without relying on a provider payload that is cleared after delivery.GET /v1/orgs/{slug}/billing/usage/auditsreturns completed claimed pre-account and Site-audit receipts with Site, time, serving model, and charging terms. Included rows name Pre-account audit or First Site audit and show $0.00; paid repeats retain the dollar charge for successful answer checks and their model-funding terms.GET /v1/orgs/{slug}/billing/usage/emailreturns the current month's received, sent, and forwarded delivery-unit rows with Site, quantity, charge state, and durable receipt. Received mail is marked not charged, and no forwarding row is represented as model-credit usage.
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. Inbound messages run the same full Wang runtime selected for the organization. When a paid-plan run needs a sensitive approval or structured answer, the durable request is shown in the TellWang dashboard.
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— OpenAI-compatible model gateway. Every plan acceptsprivate,deepseek-v4-flash, anddeepseek-v4-pro; paid plans also acceptkimi-k2.7-code. Private and Kimi support text andimage_urlcontent blocks; DeepSeek is text-only. Private never sends a failed request to an external fallback. Free and paid plans without a collectible authorization use prepaid credits. The request count is an included abuse allowance rather than a priced overage meter: paid plans and Free with prepaid credit or preserved paid key value continue after the included count while token admission remains the spend boundary. An active or trialing paid self-serve subscription with its exact authorized default card continues at zero prepaid balance: the request freezes that customer and subscription before provider spend, preserves sub-cent accrual on that identity, and sends completed cents through the subscription invoice. Card changes, cancellation scheduling, collection pauses, or an unresolved payment make fresh overage fail closed to prepaid/402. BYOK remains provider-paid and bypasses this charge.CP_LLM_CONCURRENCY_LIMITEDasks the caller to wait for an admitted completion;CP_LLM_REQUESTS_EXHAUSTEDmeans Free exhausted the included request count without usage funding. On success the response carriesX-LLM-Tokens-Remaining+X-LLM-Requests-Remaining.- 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. Valid chunks pass through unchanged; when a private provider omits the required string ID on a streamed tool call, TellWang supplies a stable opaque ID so OpenAI-compatible clients can continue. After a client disconnect, an admitted operator-funded call stops forwarding but drains the bounded provider response and settles its final authoritative usage; BYOK keeps ordinary client cancellation. GET /v1/llm/v1/models— OpenAI-spec, plan-aware discovery; returns{object:"list", data:[{id, object:"model", created, owned_by}]}. Free lists Private and both DeepSeek ids; paid plans also list Kimi.