Tether

Tether is the governed execution layer between AI and your product.

your-app — Analyst panel
Analyst live
How did the summer sale do?
rendered from blocks[] — your components
Revenue$48.2k+12%
Orders1,204
  • Linen shirt — white$9.4k
  • Canvas tote$6.1k

Revenue is up 12% on the last period — the discount drove 34% of volume.

$0.0041 · 2 tool calls · 1.8 s
this panel calls → POST /v1/chat→ 200 · blocks[kpi, stacked_bar, table, text] — rendered above
your-app — ⌘K
⌘K15% off all summer products
Staged — not executedcreate_discount15% off · 34 products tagged “summer” · SUMMER15Enter to confirm · esc to throw it away
Executingcreate_discountOne signed request to your endpoint. Never retried.
Executed onceDiscount SUMMER15 is live on 34 products.A second confirm would replay the stored result — nothing runs twice.
this Confirm calls → POST /v1/actions/9c1e7b6a/confirmPOST /v1/actions/9c1e7b6a/confirm — in flight …→ 200 · executed — idempotency key 9c1e7b6a stored

Do you already have an AI?

It is the only question that changes the integration. Both answers reach the same governed layer — the same tenants, capabilities, actions and approvals — so whichever you start with, you keep when the other becomes true.

You already have one

Bring your AI

You run an agent with tools, a model you chose and a key you pay for. What you do not have is anything stopping it doing something expensive at three in the morning.

  • Tether goes in front of your tools; your agent keeps running as it is
  • Proxy an MCP server, import an OpenAPI document, or register a catalog
  • Tool results never pass through Tether to a model provider
Bring your AI →

You do not

Use Tether AI

You have a product, data and a brand. You have never written a prompt and do not intend to. You want the thing your customers talk to.

  • Preview from your website in minutes, before any integration
  • Connect your systems, add actions, invite the people who approve them
  • One script tag, and it is in your brand on your site
Use Tether AI →

What stays yours, and what Tether runs.

Your database stays behind your own endpoint, and Tether supplies the model. Two territories, three crossings — all of them HTTP you can read. Watch a turn cross the line.

Yours — and it stays yoursyour network
Your UI

renders typed blocks; owns every confirm button

Your users

your ids ride the headers; credentials never travel

Your business logic

tools are your code, running in your runtime

Your database

no connection string exists to leak

Your permissions

checked by your endpoint, on every call

Your workflows

approvals live where they always did

Tether’s — so you never build itthe governed layer
Routing

intent decided before any model is called

Budget

quota and spend breaker, checked first

The model

provider key is Tether’s — you don’t bring one

Staged writes

mutations pause for a human

Output contract

blocks validated, repaired, or degraded safely

Spend ledger

every token priced and attributed

Turn complete. Three crossings, all of them HTTP you can read — and everything on the left ends the turn exactly where it started. $0.0018

A turn runs when this scrolls into view.

One turn, six checkpoints.

Those surfaces are the front. This is the same turn X-rayed — none of it is your UI. Your users only ever see the question going in (2) and the blocks coming back (5); everything between is Tether’s machinery, labeled in reading order.

01Route

Intent resolved with pattern rules — no model call. Cheap turns stay cheap.

02Gate

Plan quota, then the spend breaker — refused before the call, not billed after.

03Ground

Cached context, remembered preferences, retrieval over your docs.

04Call your tools

A signed envelope to your endpoint. Results feed back into the loop.

05Stage

Writes stop here as pending actions. Your endpoint is not called.

06Return

Validated typed blocks, pending actions, and the exact cost of the turn.

the wire — the same turn, X-rayed (simulated)

1 who’s calling — identity comes from the API key, never the request body

X-Api-Key tth_9f…e2 · tenant 42 · scope 9 · plan growth

2 your user’s question — free text, typed in your product

Pick a prompt to run the real flow —
↑ canned prompts so this demo can run — a real turn takes anything your user types

3 the governed path — every turn crosses the same six checkpoints, in order

4 what Tether did — the turn’s trail, also stored as audit + ledger rows

    5 what your UI gets back — typed blocks and the exact cost; you render them

    Typed blocks render here — pick a prompt above.

    your-app — what your user sees
    Assistant connected
    Refund order #4821
    Staged a $129.90 refund for order #4821. Nothing happens until you confirm it.
    rendered from pending_actions[0]
    Pending actionrefund_order · #4821$129.90 to the original card · expires in 14 min
    High risk — explicit acknowledgement requiredThe model proposed this. Your endpoint has not been called.
    POST /v1/actions/41af22c0/confirmCompare-and-set claimed it — one signed request, never retried.
    Executed oncemode: execute · X-Tether-Idempotency-Key: 41af22c0Go on — try to double-execute it.Replayed. The idempotency key matched a prior success — the stored result came back instantly and nothing ran twice.
    this button calls → POST /v1/actions/41af22c0/confirmPOST /v1/actions/41af22c0/confirm — in flight …→ 200 · executed — your endpoint received mode: execute→ 200 · replayed — stored result returned, nothing ran twice

    Every write waits for a human.

    A write tool never calls your endpoint during a turn. It becomes a pending action; someone confirms; a compare-and-set claims it; it executes once.

    That’s your product on the left — the card arrives off pending_actions, styled like the rest of your UI. Confirm the refund, then try to confirm it again.

    Learn about staged writes →

    It reaches your data through an endpoint you own.

    One signed HTTPS request to a URL you control, with arguments you can read before you answer. There is no connection string for anyone to leak.

    The amount in the body is a real input — type your own and watch the request die at your door.

    Learn about the trust boundary →
    your-app.com — the signed envelope
    POST /tether/invoke
    X-Tether-Signature:       sha256=9d41…c7   // over the original bytes
    X-Tether-Timestamp:       1755691200
    X-Tether-Idempotency-Key: 41af22c0
    
    {
      "tool": "refund_order",
      "arguments": { "order_id": "4821",
                     "amount":  },
      "tenant": { "organization_id": 42, "scope_id": 9 },
      "is_write": true, "mode": "execute"
    }
    Your endpoint verifieshmac(secret, ts + "." + body)
    ✗ signature mismatch — the body no longer matches what Tether signedSame number, different bytes — the HMAC covers the body verbatim.→ 401 rejected. No refund happens.
    ✓ signature valid → 200, refund processed by your code
    — or click the amount and type your own
    ledger — run turns until the breaker trips

    ledger · tenant 42 · append-only

    • best sellers — read$0.0041
    • refund #4790 — write$0.0029
    monthly budget$10.85 / $12.00

    breaker · refused before the call — budget_exceeded

    Spend is capped before the model is called.

    Every turn writes an append-only ledger row — input, cached, cache-write and reasoning tokens priced separately. Budgets are checked before the model is called, and an unpriced model bills at the worst known rate, never zero.

    Keep running turns until the breaker stops you.

    Learn about turns and ceilings →

    Reads fan out. Writes wait.

    The model is offered exactly the catalog you registered — nothing else exists for it to call. Read tools run in parallel through signed envelopes and stream back into the turn.

    The write lane stops at the confirm ring until a person decides. Watch it hold while the reads finish around it.

    Learn about reads and writes →
    the tool plane — one hub, your tools
    your backendPOST /v1/chatroute · gate · signget_low_stockreadget_salesreadsearch_itemsreadreads — parallel, signed, retriedconfirmrefund_orderwritewrites — held until a person confirms

    Where each boundary is enforced.

    Writes are a field you set

    { "name": "refund_order",
      "is_write": true,
      "risk": "high", … }

    Whether a tool can mutate is declared at registration — and a catalog that leaves it ambiguous is rejected before it can ship.

    Learn about the catalog →

    Answers come back as typed blocks

    "blocks": [
      { "type": "kpi", … },
      { "type": "table", … } ]

    Answers validate against a schema, get repaired once if malformed, then degrade to plain text. Your frontend renders blocks instead of parsing prose.

    Learn about the contract →

    Isolation starts with the credential

    tenant  ← api key
    body.organizationId  ignored
    tool args org_id     ignored

    Scope comes from the API key. The request body and the model’s arguments are ignored, and confirmation re-checks the boundary before any write runs.

    Learn about tenancy →

    Integration is three moves.

    Your data stays where it is, and anything that can serve HTTPS can hold the other end of the tether. If yours is Node, Python or .NET, a library will write the awkward parts for you.

    PUT /v1/tools — the catalog
    PUT /v1/tools
    [ { "name": "get_low_stock",
        "description": "Items under their alert point.",
        "is_write": false, "parameters": { … } },
    
      { "name": "refund_order",
        "description": "Refunds an order.",
        "is_write": true, "risk": "high",
        "parameters": { …, "required": ["order_id"] } } ]
    → 200 · registered · version 4 · 12 tools, 3 writes

    Whether a tool can mutate is declared at registration, never inferred — that declaration is what routes it into the staged-write path. The same validation runs standalone at POST /v1/tools/validate, so a broken catalog fails your CI instead of your users.

    your-app.com/tether/invoke — yours, any language
    app.post("/tether/invoke", (req, res) => {
      verifyHmac(req)   // sha256 over timestamp.body
    
      const { tool, arguments: args, tenant } = req.body
      assertAllowed(tenant.user_id, tool)  // your rules
    
      res.json(runTool(tool, args, tenant)) // your code,
    })                                      // your database
    → writes arrive only after confirmation · mode: "execute" · stable idempotency key

    Tether reaches your data one way: this request. Your permission checks run here, on every call — and a write only ever arrives after a human confirmed it, carrying mode: execute and an idempotency key you can dedupe on. In Node, Python or .NET, an SDK owns the signature, the timestamp window, that execute-mode check and the replay — and you write only the function.

    POST /v1/chat — one turn
    POST /v1/chat
    X-Api-Key: tth_9f…e2         // server-to-server
    X-Tether-User-Id: 7          // your user, your ids
    
    { "prompt": "Which products are almost out of stock?" }
    
    → { "blocks": [ { "type": "kpi", … },
                   { "type": "table", … } ],
        "pending_actions": [], "usage": { "usd": 0.0018 } }
    → typed blocks your UI renders · anything staged waits in pending_actions

    Your backend forwards the prompt; Tether routes it, gates it, calls your tools, and returns blocks your UI renders — seven types, rendered once, styled like your product. No iframe, no widget, nothing of ours in your page.

    The questions we get asked.

    Do I need my own model or an OpenAI key?

    No. Tether supplies and operates the model — which one runs, and how many tokens and tool turns it may spend, comes from your plan. You bring a tool catalog and one HTTPS endpoint. Every turn returns its exact cost, priced from an append-only ledger.

    Who builds the chat UI?

    You do — Tether is headless on purpose. Turns return seven typed block types (text, KPI, table, ranked list, stacked bar, footnote, quick replies) plus any pending actions to confirm. Render each type once and every answer arrives styled like your product. No iframe, no widget.

    Is there an SDK, or do I write the endpoint by hand?

    Either. The endpoint is a signed POST and a JSON body, and the docs show it implemented by hand in about twenty lines. There are also libraries for Node, Python and .NET that own the four parts that are easiest to get wrong — the signature, the timestamp window, refusing a write that did not arrive confirmed, and replaying a repeated request without running your function twice — leaving you the tool functions. None is on a registry yet; pilot customers get the source. Nothing about the integration requires one.

    Does Tether see my data?

    Tether holds no credentials to your systems. Within a turn it sees exactly what your endpoint chooses to return for a given tool call — nothing more. It stores conversations, staged actions, spend and telemetry; it has no tables for your business records.

    Can the model delete something directly?

    No. A tool declared as a write never calls your endpoint during a conversation. It becomes a pending action, a person confirms it through a separate authenticated call, a compare-and-set claims it, and it executes once.

    What happens if my tool endpoint is down?

    The tool call fails as a structured result the model can explain — “that lookup failed” — rather than crashing the turn. Reads are retried up to three times on transient failures with backoff.

    Why are writes never retried?

    A timed-out write is ambiguous: it may have landed with only the response lost. Retrying it is how one confirmation becomes two refunds. Every write carries a stable idempotency key, so you can retry safely on your side — where you can actually check.

    How are tenants isolated?

    Scope comes from the API key — never from the request body, never from the model’s tool arguments. Catalog, endpoint, budget, history and memory are all per-tenant, and confirmation re-checks the tenant boundary before any write executes.

    What if a tool returns too much data?

    Responses are capped at 256 KB. Past that, the result is refused and the model is told, instead of an oversized payload poisoning the context window and billing you for it.

    Which models does Tether support?

    One provider today, stated plainly. The boundary — staged writes, budgets, isolation, typed output — is designed to outlive whichever model sits behind it, but multi-provider support does not exist yet.

    Can I self-host?

    Not yet. There is no packaged self-hosted distribution today.

    What does Tether store?

    Conversations, staged actions and their execution records, an append-only spend ledger, request telemetry, extracted user preferences, and any documentation you ingest for retrieval. Orders, customers and inventory stay in your systems.

    Put AI behind a boundary you control.