API reference
Providers
A software provider, such as a booking or shop platform, serves many businesses. Each business is its own tenant with its own keys, approvers, policy and audit. The provider creates them, starts them from its own configuration and looks after their keys. It never reads their conversations, actions or audit, and it can never reach a tenant it did not create.
Becoming a provider
Provider authority is granted by Tether, not taken. Your platform is first an ordinary tenant
with a secret key. When Tether grants it provider authority, you receive a provider key, prefix
tpv_, shown once.
The grant is an operator call, made with the deployment's operator key:
curl -X POST https://api.trytether.ge/v1/tenants/$PROVIDER_TENANT_ID/provider \
-H "X-Api-Key: $TETHER_OPERATOR_KEY"
{ "tenantId": 7, "provider": true, "providerKey": "tpv_…", "prefix": "tpv_3f9a" }
Calling it again issues another provider key and leaves the grant as it is. DELETE on the same
route withdraws the grant and answers revokedKeys. A business a provider created cannot itself be
made a provider (409 provisioned_tenant).
| Key | Reaches |
|---|---|
Your secret key, tth_ | Your own tenant: the configuration your businesses start from |
Your provider key, tpv_ | /v1/provider, and /v1/user-sessions to sign your businesses' own users in to an approval app |
Your inbox key, tib_ | Signing your businesses' users in and, in their sessions, their approval routes. Nothing else. Issue it with POST /v1/provider/keys for the host that runs the approval inbox |
Your CI key, tci_ | Publishing your repository's contract draft, docs and vocabulary suggestions to your own tenant, the template, from your CI runner. Issue it with POST /v1/provider/keys {"kind": "ci"}; see Repository |
The two are kept apart on purpose. Your secret key cannot create a business (403 insufficient_scope), and your provider key cannot chat, register tools, read an audit or reach
any operator route. No other credential reaches /v1/provider. Tether can withdraw provider
authority. Your provider keys and inbox keys then stop at once, and your businesses keep running
with their own keys. Your secret key cannot rotate or revoke a provider or inbox key either; only
the operator issues provider keys, and only your provider key manages your inbox keys.
The configuration your businesses start from
Configure your own tenant with your secret key, exactly as a single business would: import your API or register your catalog, then set the domain pack and the approval policy. You can try it on your own tenant before any business uses it.
When you create a business, it gets a copy of what your tenant currently has:
| Part | Copied |
|---|---|
| Catalog | The current version |
| Domain pack | The current version |
| Approval policy | The current version |
| Upstream connections | The imported API or MCP server, with its credential (still encrypted at rest) |
| Tool endpoint, events webhook and allowed origins | When set. Events for every business then reach your one receiver, each signed with that business's own signing secret |
| Knowledge documents | Each one, with its embeddings, as part knowledge:<name>. It replaces the business's document of the same name and leaves its others; the business sees copiedFrom on it and may delete it |
| Users (approvers) | Never. A business registers its own staff |
A business does not follow your later changes on its own. Editing your catalog cannot change what a business's agent may do in the middle of its conversations. To bring a business up to date, apply your configuration again. Each part you have configured becomes a new version in that business, and its earlier versions stay readable. A part you have not configured is left as the business has it.
Create a business
curl -X POST "$TETHER_URL/v1/provider/tenants" \
-H "X-Api-Key: $TETHER_PROVIDER_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Venue One" }'
{
"tenantId": 31,
"name": "Venue One",
"plan": "use_tether_ai",
"providerTenantId": 12,
"apiKey": "tth_5b1e…",
"keyPrefix": "tth_5b1e2c4f",
"signingSecret": "9d0a…",
"customerTokenKey": "tck_77c3…",
"customerToken": { "algorithm": "HS256", "header": "X-Tether-Customer-Token", "maxLifetimeSeconds": 3600 },
"configuration": [
{ "part": "catalog", "providerVersion": 3, "tenantVersion": 1 },
{ "part": "domain_pack", "providerVersion": 2, "tenantVersion": 1 },
{ "part": "policy", "providerVersion": 1, "tenantVersion": 1 },
{ "part": "upstream:api", "providerVersion": null, "tenantVersion": null }
]
}
apiKey, signingSecret and customerTokenKey are shown once. Your server signs this
business's customer tokens with its customerTokenKey, with
tenant set to its tenantId. Each business has its own customer-token key and signing secret, so a
leak at one business cannot be used at another. name is required, up to 120 characters. A
business runs on your plan.
Then register the business's own approvers with its secret key, through
PUT /v1/users. Only those people can approve its actions. Your own staff
cannot, even though your tenant's policy is the business's starting point.
Routes
| Method and path | What it does |
|---|---|
POST /v1/provider/tenants | Creates a business, as above |
GET /v1/provider/tenants | Your businesses: tenantId, name, plan, active, catalogVersion, customerTokenKeyIssued, createdAt |
GET /v1/provider/tenants/{id} | One of them |
POST /v1/provider/tenants/{id}/configuration | Applies your current configuration to it again. Answers the copied parts |
GET /v1/provider/tenants/{id}/keys | Its keys by prefix, kind, label and dates. Never the key itself |
POST /v1/provider/tenants/{id}/keys | Issues a key. Body: { "kind": "secret" | "publishable" | "agent" | "inbox", "label": "..." } |
POST /v1/provider/tenants/{id}/keys/{prefix}/rotate | Issues a replacement, then revokes the old key |
DELETE /v1/provider/tenants/{id}/keys/{prefix} | Revokes a key. Its only live secret key cannot be revoked |
POST /v1/provider/tenants/{id}/customer-token-key | Replaces its customer-token key. Tokens signed with the old one are refused at once |
GET /v1/provider/outcomes | Each business's outcome report and their total, as counts only. from, to, Accept: text/csv |
GET /v1/provider/keys | Your own inbox and CI keys by prefix, kind (provider_inbox, ci, ci_replace), label and dates |
POST /v1/provider/keys | Issues an inbox key, tib_, shown once, for every business you created. Body: { "kind": "inbox", "label": "..." }. With { "kind": "ci" } (and optionally "allowReplace": true) it issues a CI key, tci_, on your own tenant, which your repository's runner publishes the template with |
POST /v1/provider/keys/{prefix}/rotate | Issues a replacement inbox or CI key, then revokes the old one |
DELETE /v1/provider/keys/{prefix} | Revokes an inbox or CI key; it stops on the next request |
Key limits are the same as on Account: ten live keys per business. A provider key cannot be issued for a business.
What a provider cannot do
- Reach a tenant it did not create. Another provider's business, a business that came to Tether directly, your own tenant and an id that does not exist all get the same answer, so the response does not tell you which tenants exist:
{ "error": { "code": "not_a_child_tenant", "message": "No tenant with that id was created by this provider." } }
- Read a business's conversations, actions, approvers or audit. Those routes need the business's
own key, and a provider key gets
403 insufficient_scopethere. What a provider does see is the outcome report of each business it created: counts of conversations, actions, handoffs and cost, with no conversation, action or person in them. - Approve a business's actions. Approvals come only from users the business registered. An approval app you run for your businesses, such as the approval inbox, signs those users in through user sessions: with one, your key reaches one business's approval routes as that business's signed-in user, and without one, nothing.
- Make a business into a provider. Tether grants provider authority to a platform, never to a business a provider created.
Errors
| Status | Code | Meaning |
|---|---|---|
400 | invalid_request | name is missing or longer than 120 characters |
400 | unknown_kind | kind is not secret, publishable, agent or inbox (on /v1/provider/keys, not inbox) |
403 | insufficient_scope | Not a provider key, or a provider key on a route outside /v1/provider and /v1/user-sessions without a user session |
403 | not_a_provider | The tenant no longer holds provider authority |
404 | not_a_child_tenant | That id is not a business this provider created |
404 | unknown_key | No live key with that prefix on that business |
409 | key_limit_reached | That business already has ten live keys |
409 | last_secret_key | That is the business's only live secret key; rotate it |
409 | provisioned_tenant | POST /v1/tenants/{id}/provider (operator): the tenant was created by a provider |