TellWang
Dashboard

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.

base
https://api.tellwang.com/v1            # control plane (Woks, resources)
https://<wok-id>.tellwang.com/rest/v1  # a Wok's data API

Authentication

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
curl https://api.tellwang.com/v1/woks \
  -H "Authorization: Bearer <api-key>"

Conventions

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.

POST /v1/woks
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" }
Prefer not to manage keys? Tell Wang or use the MCP server — same API underneath, with auth handled for you.

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.

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.

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.

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.

Low-level capture boundary: these site-clone endpoints stop at capture, manifest/artifact reads, passive-asset rehosting, and cleanup. The committed Shopify journey composes separate connections, internal brokers, and the fixed worker for import, compile, Preview deployment, and parity. No low-level capture response is a one-to-one migration claim.

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.

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.

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.

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.

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:

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.

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

Edge functions

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

Scheduler

Environment variables

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.

paste-dotenv.sh
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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.