Tether
DocsGitHub

Guides

The approval inbox

A staged write does nothing until someone approves it. On the second door the person who asks and the person who approves are different people from the first write — a shopper asks for a refund, someone in finance approves it — so the approving has to happen somewhere.

What it shows

The queue of staged writes, each with the tool, the arguments it would run with, its declared risk, which scope it belongs to, how old it is and how long is left before it expires, the verified customer it is for, and the policy rule that put it in front of this person, with its condition ("change_booking when partySize ≤ 6 needs host or manager"). Filter by tool or scope. After an approval it says what reading the result back found (outcome verification), and a refusal says why, such as a precondition that no longer holds. Recently applied writes are listed with the same check. A history tab reads the audit trail, with each event's customer and reason.

Approve is disabled, and says why

When your policy does not permit this person's role, the button is disabled and the reason sits next to it:

Your role (support) cannot approve this. It is waiting for finance.

A control that appears and then fails has already told someone it exists. The inbox checks the policy before offering the button — but that is a courtesy, not the boundary. The engine re-decides against the same policy and refuses anything the inbox lets through, so a disagreement produces a refused confirm, never an unauthorised one.

A rule with a condition on arguments is matched over the arguments shown on the row, so a refund of 40 can be enabled for support while a refund of 250 beside it waits for finance. The inbox and the engine choose the rule from the same shared cases, so they agree on which rule applies.

A tenant with no registered policy is not enforcing one, and the inbox agrees: every button works, exactly as the engine behaves.

Signing in

Magic links, issued, mailed and spent by Tether through user sessions. Someone enters their work address, and Tether sends one email for each business where they are a registered user, naming the business, in its language, with its own link; a person who works at two venues opens the one they mean. Each link is good for ten minutes and opens once, whichever instance of the inbox it reaches. The inbox never sees a link, and no password is held.

Only someone a business has already registered can be sent a link, so signing in cannot invent an approver. An address that is not registered gets the same answer as one that is, and no email.

A person a business invited by email gets their first link in the invitation itself, good once for 24 hours: opening it signs them in and lands on that business's queue. They need no account in the business's product and no user ID; the inbox knows them by the id Tether gave them, and the history names people by name and address.

Tether also emails each person whose role may approve a newly staged write, under the business's policy and its conditions, that it is waiting, with a link to the inbox. Where those links point, and which mail server sends them, is set by whoever runs Tether (Mail), not by the inbox: its MAGIC_LINK_TRANSPORT and INBOX_URL settings are gone, and the inbox refuses to start while either is set.

One deployment for many businesses

The inbox runs on an inbox key, TETHER_INBOX_KEY=tib_…, and nothing per business. A software provider issues one for every business it serves with POST /v1/provider/keys; a single business issues its own with POST /v1/account/keys and "kind": "inbox". Every call carries the signed-in person's session, and Tether answers as that person, in their own business, with the role the business registered for them. The key alone approves nothing and reads no business's actions or audit. Run as many instances as you like behind one INBOX_SECRET: spent links and sessions live in Tether, so they agree, and signing out at one ends the session at all of them.

What the inbox key cannot do

An inbox key asks Tether to mail sign-in links and, inside one of their sessions, reaches the approval routes. It cannot be handed a link (POST /v1/user-sessions/links), create a business, issue or rotate a key, apply a configuration, register tools, change a policy, manage users, chat, reach MCP or set an events webhook: each answers 403 insufficient_scope with error.reason not_an_inbox_route, and an approval route called without a session answers user_session_required. So the host the inbox runs on holds nothing that can change what a business's agent may do. The inbox refuses a provider key (tpv_) or a secret key (tth_), and refuses to start a call while TETHER_PROVIDER_KEY or TETHER_API_KEY, which earlier versions read, is set. Rotate or revoke the inbox key through the route that issued it; a provider whose authority is withdrawn loses its inbox keys on the next request.

Where the key lives

The key is a server credential and never reaches a browser. The page talks to the inbox's own server, which adds the key and the session and calls Tether. An inbox tab left open on a shared machine leaks a session, not a key.

Every control, and the endpoint behind it

ControlEndpoint
The queueGET /v1/actions
Risk badgeGET /v1/tools
Whether Approve is enabledGET /v1/policy
ApprovePOST /v1/actions/{id}/confirm
RejectPOST /v1/actions/{id}/reject
HistoryGET /v1/audit
Sign inPOST /v1/user-sessions/sign-in (Tether mails the link), then POST /v1/user-sessions
Sign outDELETE /v1/user-sessions/current

Nothing else. You can build your own against exactly these.