Agent onboarding
Put a verified person behind a workspace an AI agent registered, hand it to them, and read what an agent needs on day one: usage, guides and languages.
9 endpoints
How an agent gets a workspace
- The agent calls
registerWorkspaceon the TotalPath MCP server. It needs no key and gets back a new workspace and an API key. The workspace can create records but cannot place a call yet. - A person agrees to stand behind the workspace. The agent calls Send a verification code with their mobile number, the person reads the SMS code back, and the agent calls Verify the code. That verified person is the workspace's anchor, and the first anchor of a new number brings free calling capacity.
- To hand the workspace to a person, so they can sign in and pay for more capacity, the agent calls Get a claim link and gives them the link.
The same steps are MCP tools (requestAnchorCode, verifyAnchorCode, getClaimLink, getWorkspaceUsage, listGuides), so an agent using the MCP server does not need these routes. They are here for a client that calls the API directly. None has an SDK method yet, so the examples are cURL only.
Send a verification code
/anchors/request-codeSend a one-time code by SMS to a person's mobile, to verify that a real person stands behind this workspace (its "anchor"). This is what unlocks calling capacity on an agent-registered workspace. The code goes to the phone, not to the caller: ask the person to read it back, then call Verify the code. Every call sends a real text message to a real person, so it is rate limited per hour (429) and over the workspace's lifetime (403, which does not lift). The workspace comes from the API key, never the body. 400 with a `reason` when the number is not one TotalPath will anchor (a landline, or a country that is not supported). 403 `anchor_limit_reached` when the workspace already holds the most anchors it may. 409 `already_anchored` when the number is the anchor of a different workspace. Needs the `anchors:write` scope, or a key with no scopes.
Body Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| phoneNumber | string | required | The mobile to anchor, in E.164, e.g. +447700900123. Mobiles in the United Kingdom, Guernsey, Jersey, the Isle of Man or the United States |
Request
curl -X POST -H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phoneNumber":"+447700900123"}' \
https://api.nopaque.co.uk/anchors/request-codeResponse
{
"sent": true
}Verify the code
/anchors/verify-codeCheck the code the person read back and, if it is right, record the number as this workspace's anchor. The number is stored only as an HMAC. On the first anchor of a number that has never earned one, the workspace is also granted its free calling capacity, and `freeGrantOutcome` says what happened: `granted` (capacity added now), `already-granted` (the workspace already had it), `not-owed` (this number has used its one free allocation before, so capacity has to be bought: see Get a claim link), `refused` (declined, see `freeGrantError`) or `unavailable` (the code was right but the grant could not be completed; call again with the same body). 400 `bad_code` when the code is wrong or has expired. A 5xx is a provider problem, never a wrong code. Send `replaces` to hand over from an existing anchor to this new person in one step. Needs the `anchors:write` scope, or a key with no scopes.
Body Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| phoneNumber | string | required | The same number the code was sent to, in E.164 |
| code | string | required | The code from the text message |
| replaces | string | optional | An E.164 number already anchored to this workspace. The new number is anchored and this one released in one step, so the workspace is never left without an accountable person |
Request
curl -X POST -H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phoneNumber":"+447700900123","code":"482913"}' \
https://api.nopaque.co.uk/anchors/verify-codeResponse
{
"anchored": true,
"freeGrantOwed": true,
"freeGranted": true,
"freeGrantOutcome": "granted"
}List anchors
/anchorsHow many people stand behind this workspace, when each verified, and the most it may hold (`maxAnchors`). Deliberately carries no phone number and no hash. A workspace with no anchors returns an empty list. Needs the `anchors:write` scope, or a key with no scopes.
Request
curl -H "x-api-key: YOUR_API_KEY" \
https://api.nopaque.co.uk/anchorsResponse
{
"anchors": [
{
"anchoredAt": "2026-11-12T09: 14: 00Z"
}
],
"maxAnchors": 5
}Release an anchor
/anchors/releaseFree the anchor on `phoneNumber`, keeping `replacedBy` as the workspace's accountable person. Both numbers must already be anchored to this workspace, so no SMS is sent. `replacedBy` is required by design: an anchor is only released by another taking its place, so a workspace is never left with nobody accountable for the calls it places. To hand over to someone new, use Verify the code with `replaces` instead. The anchor record is kept, so past calls stay attributable, and the number's free allocation is not restored. 409 `not_held` when the workspace does not hold one of the two numbers. Needs the `anchors:write` scope, or a key with no scopes.
Body Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| phoneNumber | string | required | The anchored number to release, in E.164 |
| replacedBy | string | required | Another number already anchored to this workspace, which stays as its accountable person |
Request
curl -X POST -H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phoneNumber":"+447700900123","replacedBy":"+447700900456"}' \
https://api.nopaque.co.uk/anchors/releaseResponse
{
"released": true
}Get a claim link
/claimMint a single-use link, valid for one hour, that a person opens to take ownership of an agent-registered workspace: they set their own sign-in and can add payment. The agent can create the link but never use it. The token is in the URL fragment (after `#t=`), so it is never sent to a server or logged; treat the whole URL as a credential and give it only to the person. 409 once the workspace has been claimed. 503 when the link could not be saved; try again. Needs the `claim:write` scope, or a key with no scopes.
Request
curl -X POST -H "x-api-key: YOUR_API_KEY" \
https://api.nopaque.co.uk/claimResponse
{
"url": "https://totalpath.ai/claim#t=<one-time-token>",
"expiresAt": "2026-11-12T10: 14: 00Z"
}Get workspace usage
/workspaces/{workspaceId}/usageA short usage and entitlement snapshot for the workspace: its plan, the free and purchased seconds left in each pool (testing, load testing, mapping and the two speech-to-speech pools), seconds used this billing period, and AI tokens left. Use it to check there is capacity before starting work that places calls. `workspaceId` must be the key's own workspace; any other returns 404.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| workspaceId | string | required | The workspace the API key was issued for |
Request
curl -H "x-api-key: YOUR_API_KEY" \
https://api.nopaque.co.uk/workspaces/3b1f9c2e-6a4d-4f8b-9e7c-1d2a3b4c5d6e/usageResponse
{
"workspaceId": "3b1f9c2e-6a4d-4f8b-9e7c-1d2a3b4c5d6e",
"subscriptionTier": "free",
"currentPeriodEnd": "2026-12-01T00: 00: 00Z",
"pools": {
"testing": {
"freeRemainingSecs": 1800,
"purchasedRemainingSecs": 0,
"currentPeriodUsedSecs": 240
},
"loadTesting": {
"freeRemainingSecs": 0,
"purchasedRemainingSecs": 0,
"currentPeriodUsedSecs": 0
},
"mapping": {
"freeRemainingSecs": 1200,
"purchasedRemainingSecs": 0,
"currentPeriodUsedSecs": 60
},
"s2s": {
"freeRemainingSecs": 0,
"purchasedRemainingSecs": 0,
"currentPeriodUsedSecs": 0
},
"s2stest": {
"freeRemainingSecs": 600,
"purchasedRemainingSecs": 0,
"currentPeriodUsedSecs": 0
}
},
"tokens": {
"remainingTokens": 50000,
"currentPeriodTokensUsed": 1200
}
}List guides
/guidesThe step-by-step guides to the TotalPath web app and the reference pages (pricing, FAQ) published for agents. `kind` is `guide` for a walkthrough and `reference` for a page of facts. The same for every workspace. 503 when the guides cannot be read just now.
Request
curl -H "x-api-key: YOUR_API_KEY" \
https://api.nopaque.co.uk/guidesResponse
{
"guides": [
{
"slug": "standard-create",
"kind": "guide",
"group": "Standard tests",
"title": "Create a standard test",
"summary": "Script a call step by step and check the IVR answers correctly"
},
{
"slug": "pricing",
"kind": "reference",
"group": "Reference",
"title": "Pricing - TotalPath by Nopaque",
"summary": "Published per-minute and per-plan pricing for TotalPath."
}
]
}Get a guide
/guides/{slug}The text of one guide or reference page, in order, as short markdown sections. For a guide, each section is one numbered step naming the page, menu, button or field to use. The steps are for a person to follow in the web app. 404 for an unknown slug.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| slug | string | required | A slug from List guides |
Request
curl -H "x-api-key: YOUR_API_KEY" \
https://api.nopaque.co.uk/guides/standard-createResponse
{
"slug": "standard-create",
"kind": "guide",
"group": "Standard tests",
"title": "Create a standard test",
"summary": "Script a call step by step and check the IVR answers correctly",
"sections": [
"## 1. Start on the Dashboard\n\nYou land on the Dashboard when you sign in. Everything to do with tests sits behind Testing, in the left-hand sidebar."
]
}List languages
/languagesBoth of the platform's language sets, with their codes, the labels to show a person, and each set's default. The two sets are never merged: `transcription` is what a standard test or a mapping run is transcribed in (default `en-GB`); `call` is what a mission test is spoken in (default `en`). Bare `en` is a call language and is rejected as a transcription language; `en-GB` is not a call language at all. Pick the set by what the value configures. `models` and `model` are informational: TotalPath picks the model, and no request takes one. The same answer for every caller, served with `Cache-Control: max-age=3600`. A `400 Invalid language` elsewhere names the set it wanted; this adds the labels.
Request
curl -H "x-api-key: YOUR_API_KEY" \
https://api.nopaque.co.uk/languagesResponse
{
"transcription": {
"default": "en-GB",
"languages": [
{
"code": "en-GB",
"label": "English (UK)",
"models": [
"nova-2",
"nova-3"
],
"codeSwitching": false
},
{
"code": "multi",
"label": "Auto / multilingual",
"models": [
"nova-3"
],
"codeSwitching": true
}
]
},
"call": {
"default": "en",
"languages": [
{
"code": "en",
"label": "English",
"model": "flux",
"codeSwitching": false
},
{
"code": "multi",
"label": "More than one language",
"model": "flux-multi",
"codeSwitching": true
}
]
}
}