{"openapi":"3.1.0","info":{"title":"NuTalk API","version":"1.0.0","summary":"AI voice and WhatsApp agents: agents, calls, contacts, campaigns and evaluation.","description":"REST API for a NuTalk workspace. Every request carries a workspace API key as `Authorization: Bearer sk_<prefix>_<secret>`; keys are created in the dashboard (Settings → API keys) and limited to scopes.\n\nScopes: `clinic_setup:read`, `clinic_setup:write`, `business_setup:read`, `business_setup:write`, `appointments:read`, `appointments:write`, `agents:read`, `agents:write`, `channels:read`, `channels:write`, `phone_numbers:read`, `phone_numbers:write`, `contacts:read`, `contacts:write`, `contact_lists:read`, `contact_lists:write`, `calls:read`, `calls:write`, `conversations:read`, `conversations:write`, `campaigns:read`, `campaigns:write`, `conversions:read`, `conversions:write`, `goals:read`, `goals:write`, `competencies:read`, `competencies:write`, `testing:read`, `testing:write`, `knowledge:read`, `knowledge:write`, `billing_usage:read`, `webhooks:read`, `webhooks:write`. A key with no scopes can only call GET /auth/verify.\n\nResponses are JSON (except GET /calls/{id}/recording, which answers audio). Success: `{\"success\": true, \"data\": …}` (lists add `pagination`: total, limit, offset, has_more). Errors: `{\"success\": false, \"error\": {\"code\", \"message\", \"details\"?}}`.\n\nRate limits are per workspace, across all its keys: 1,000 requests per hour and 100 per minute by default; higher limits are available on request. RateLimit-* and X-RateLimit-* headers report the workspace's own hourly window, X-RateLimit-*-Minute the minute; a 429 carries Retry-After.\n\nResellers use a reseller API key (`Bearer rk_<prefix>_<secret>`, Settings → API keys → Reseller API keys) for the /reseller endpoints, and for the workspace endpoints of one client at a time, named by the `NuTalk-Client-Id` header (required with a reseller key, refused with a workspace key): every read, and starting or pausing a campaign. It reaches only its clients; any other client answers 404 `client_not_found`, and a suspended client's workspace answers changes with 409 `client_suspended`. Its MCP server (read-only) is https://www.nutalk.ai/v1/reseller/mcp.\n\nAI clients (Claude Code, Codex, Cursor) can use the same key with the MCP server at https://www.nutalk.ai/v1/mcp. Guides: https://www.nutalk.ai/en/docs/api and https://www.nutalk.ai/en/docs/api/ai-agents. Plain-text index for AI agents: https://www.nutalk.ai/v1/llms.txt.","contact":{"name":"NuTalk Support","email":"support@nutalk.ai","url":"https://www.nutalk.ai/en/docs/api"},"license":{"name":"Proprietary","identifier":"LicenseRef-Proprietary"}},"servers":[{"url":"https://www.nutalk.ai/v1","description":"Production"}],"security":[{"apiKey":[]}],"tags":[{"name":"Authentication","description":"Verify a key and read its scopes.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/authentication"}},{"name":"Agents","description":"Create agents, version their instructions and deploy them; set their automations, follow-up emails, transfers, knowledge and memory, and Cal.com booking.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents"}},{"name":"Calls","description":"Place outbound calls and read transcripts, summaries and outcomes.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls"}},{"name":"Conversations","description":"Read WhatsApp, SMS, Instagram, Messenger, Apple Messages and website conversations and their messages; reply, assign and resolve them.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations"}},{"name":"Contacts","description":"Create, find, update and opt out contacts; define the custom fields they hold.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts"}},{"name":"Contact lists","description":"Create lists, and add or remove their members.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-lists"}},{"name":"Contact imports","description":"Import CSV files of millions of rows in the background and follow their progress.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-imports"}},{"name":"Campaigns","description":"Create, start and pause calling campaigns; enroll and import contacts; read results.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns"}},{"name":"Conversions","description":"Read the bookings, visits, calls, goals and won deals NuTalk records, and record payments, won deals, qualified leads and your own outcomes.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversions"}},{"name":"Goals","description":"Define what success looks like for an agent, with period targets.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/goals"}},{"name":"Competencies","description":"Define rubrics the evaluator scores conversations against.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/competencies"}},{"name":"Channels","description":"List phone numbers, WhatsApp numbers and widgets; create phone channels and set their number, calls and voice; choose each channel's agent, AI mode and scheduling.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels"}},{"name":"Phone numbers","description":"List the workspace's numbers, search the numbers for sale and buy one; every purchase waits for an owner's or admin's approval.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/phone-numbers"}},{"name":"Billing","description":"Read the plan, the credits, the usage report, the credit history and the alerts; never cards or payments.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/billing"}},{"name":"Webhooks","description":"Register signed endpoints for call, conversation and conversion events, and read their delivery log.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/webhooks"}},{"name":"Clinic setup","description":"Set up clinic scheduling: locations, rooms, doctors, services, weekly hours, days off and clinic settings.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic"}},{"name":"Business scheduling setup","description":"Set up meeting scheduling: hosts, their availability and days off, and meeting types.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling"}},{"name":"Agenda","description":"List, book, reschedule and cancel clinic and business appointments, and find free times.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda"}},{"name":"Automated testing","description":"Write test scenarios for an agent, run them and read the scored results.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing"}},{"name":"Knowledge","description":"Add the documents, text and web pages an agent answers from.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/knowledge"}},{"name":"Approvals","description":"Deletes and changes that wait for an owner's or admin's approval, and what each one became.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/approvals"}},{"name":"Resellers","description":"With a reseller key: list your clients, their usage and invoices, and read each client's workspace.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers"}}],"externalDocs":{"description":"NuTalk API documentation","url":"https://www.nutalk.ai/en/docs/api"},"paths":{"/auth/verify":{"get":{"operationId":"verifyApiKey","summary":"Verify an API key","description":"Confirms the key and returns its workspace and granted scopes. Any valid key may call it, including a verify-only key with no scopes. Use it before running an integration, and read `api_key.scopes` to know what the key can do.\n\nAny valid key, including a verify-only key.\n\nVerify requests count against a rate-limit bucket of their own (1,000 per hour, 100 per minute), separate from the other endpoints.","tags":["Authentication"],"x-required-scope":null,"security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/authentication#verifyApiKey"},"parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The key is valid.","content":{"application/json":{"example":{"success":true,"data":{"authenticated":true,"api_key":{"id":"7b7a0ce4-0000-4000-8000-000000000001","name":"Production backend","prefix":"51e4f2a9","scopes":["agents:read","calls:read","calls:write"]},"workspace":{"id":"d3b85d84-ef8f-4c4a-8d18-3fa23e7fd48a","name":"Acme","slug":"acme","status":"active"},"verified_at":"2026-10-02T02:19:49.549Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents":{"get":{"operationId":"listAgents","summary":"List agents","description":"Lists the workspace's agents, newest first, with their status: `draft` until a version is first deployed, then `active`. Archived (deleted) agents are listed only with `status=archived`, and `POST /v1/agents/{id}/restore` brings one back. `deployed_version` is null until a version is deployed.\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#listAgents"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only agents in this status. Without it, every agent but the archived ones.","schema":{"type":"string","enum":["draft","active","paused","archived"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of agents.","content":{"application/json":{"example":{"success":true,"data":[{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent","description":"Handles customer support inquiries","created_at":"2026-10-02T02:20:32.316004+00:00","status":"active","updated_at":"2026-10-02T03:56:00.896+00:00","deployed_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.1.0"}}],"pagination":{"total":1,"limit":10,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createAgent","summary":"Create an agent","description":"Creates an agent and its first version (`v1.0.0`, a draft) holding these instructions, and the language, identity, purpose and call opening sent. A draft takes no calls or messages: deploy it with `POST /v1/agents/{id}/deploy`.\n\nRequires the `agents:write` scope.\n\nA version holds the name it introduces itself with, its instructions, language, purpose and call opening. The agent's automations, follow-up emails, transfers, knowledge and memory settings and Cal.com booking have endpoints of their own (`/v1/agents/{id}/automations` and the rest) and apply to its live version at once. Its voice is set on its channels. How it books (`/v1/agents/{id}/booking-method`, clinic-booking, business-booking) and the REST actions it calls (`/v1/agents/{id}/rest-connector`) have their own endpoints; its data connectors are set in the dashboard.\n\nA new agent is not on any channel: put it on one with `PUT /v1/channels/{id}/agent`. Once it answers a channel, deploying a new version moves that channel to it.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#createAgent"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Display name.","type":"string","minLength":1,"maxLength":100},"instructions":{"description":"The system prompt: what the agent does and how it speaks.","type":"string","minLength":1,"maxLength":50000},"description":{"description":"What the agent is for (shown in the dashboard).","type":"string","maxLength":500},"language":{"description":"The language the agent speaks: its calls' speech recognition and the platform's own rules follow it. Without one the agent runs in English (en-US), so set it for every agent that speaks another language.","type":"string","enum":["es-MX","en-US","es-ES","en-GB"]},"identity_name":{"description":"The name the agent introduces itself with, e.g. Sofía. Default: the agent's name up to its first separator (|, —, –, ·, /, (, :, a comma or a spaced hyphen), so `Sofía | Club Cañada — Reactivación` calls itself Sofía.","type":"string","maxLength":100},"purpose":{"description":"What the agent calls about, as it would finish \"I'm calling about…\", e.g. your appointment tomorrow.","type":"string","maxLength":500},"greeting":{"description":"The call opening: what the agent says first on a call. It may use the person's details, e.g. `{{contact.first_name|there}}` (a fallback after |).","type":"string","maxLength":500},"say_greeting_exactly":{"description":"`true`: calls start with the platform saying the opening word for word, the person's details filled in (an opening with a [slot] or a variable nothing fills is said by the AI, as written). `false`, where every agent starts: the opening is a brief the AI adapts.","type":"boolean"}},"required":["name","instructions"]},"example":{"name":"Support Agent","description":"Handles customer support inquiries","instructions":"You are the support agent for Acme. Answer in one or two sentences and offer a callback when you cannot solve the issue.","language":"en-US","identity_name":"Ava"}}}},"responses":{"201":{"description":"The agent and its draft version.","content":{"application/json":{"example":{"success":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent","description":"Handles customer support inquiries","created_at":"2026-10-02T02:20:32.316004+00:00","version":{"id":"c5c0810c-4614-4f0d-927d-62e587dad45e","version_label":"v1.0.0","deployment_status":"draft"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}":{"get":{"operationId":"getAgent","summary":"Get an agent","description":"Returns an agent with its deployed version's instructions, language, identity, purpose and call opening, and every version's label and status.\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgent"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The agent.","content":{"application/json":{"example":{"success":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent","description":"Handles customer support inquiries","created_at":"2026-10-02T02:20:32.316004+00:00","updated_at":"2026-10-02T03:56:00.896+00:00","deployed_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.1.0","instructions":"You are the support agent for Acme. Answer in one or two sentences.","language":"en-US","identity_name":"Ava","purpose":null,"greeting":"Hi {{contact.first_name|there}}, this is Ava from Acme.","say_greeting_exactly":true},"versions":[{"id":"c5c0810c-4614-4f0d-927d-62e587dad45e","version_label":"v1.0.0","deployment_status":"draft","created_at":"2026-10-02T02:20:32.324764+00:00"},{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.1.0","deployment_status":"deployed","created_at":"2026-10-02T02:21:20.112042+00:00"}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateAgent","summary":"Update an agent","description":"Changes the name or description at once. New instructions, language, identity, purpose or call opening are saved together as one new draft version that copies the rest of the latest version's configuration; the deployed version keeps answering until you deploy the new one. The draft's label is the agent's highest version one minor step up, whichever way its versions were made: v3.11.1 → v3.12.0 (a label from before every version was vX.Y.Z counts as vX.Y.0: v3.10 → v3.11.0). The answer names that draft (`version_id`, `version_label`): deploy it with POST /v1/agents/{id}/deploy and that `version_id`.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgent"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"New display name.","type":"string","minLength":1,"maxLength":100},"description":{"description":"New description; an empty string clears it.","type":"string","maxLength":500},"instructions":{"description":"New system prompt, saved as a new draft version.","type":"string","minLength":1,"maxLength":50000},"language":{"description":"The language the agent speaks: its calls' speech recognition and the platform's own rules follow it.","type":"string","enum":["es-MX","en-US","es-ES","en-GB"]},"identity_name":{"description":"The name the agent introduces itself with. An empty string goes back to its name up to the first separator.","type":"string","maxLength":100},"purpose":{"description":"What the agent calls about, as it would finish \"I'm calling about…\", e.g. your appointment tomorrow. An empty string clears it.","type":"string","maxLength":500},"greeting":{"description":"The call opening: what the agent says first on a call. It may use the person's details, e.g. `{{contact.first_name|there}}` (a fallback after |). An empty string removes it.","type":"string","maxLength":500},"say_greeting_exactly":{"description":"`true`: calls start with the platform saying the opening word for word, the person's details filled in (an opening with a [slot] or a variable nothing fills is said by the AI, as written). `false`, where every agent starts: the opening is a brief the AI adapts.","type":"boolean"}}},"example":{"description":"Handles support and sales","instructions":"You are the front desk for Acme. Answer in one or two sentences."}}}},"responses":{"200":{"description":"The updated agent, with the draft version the change made (`version_id`, `version_label`) when it made one.","content":{"application/json":{"example":{"success":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent","description":"Handles support and sales","created_at":"2026-10-02T02:20:32.316004+00:00","updated_at":"2026-10-02T03:56:00.896+00:00","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.2.0"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgent"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent books in more than one way and nobody chose its booking method (`details.reason` `booking_method_conflict`): choose it in the agent's Actions tab first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteAgent","summary":"Delete an agent","description":"Archives the agent and takes it off every channel it was on. Those channels stop answering with it right away, so connect another agent first. An archived agent can't be fetched or changed; `GET /v1/agents?status=archived` lists it and `POST /v1/agents/{id}/restore` brings it back.\n\nRequires the `agents:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#deleteAgent"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The agent is archived.","content":{"application/json":{"example":{"success":true,"data":{"deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteAgent"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/deploy":{"post":{"operationId":"deployAgent","summary":"Deploy an agent version","description":"Makes a version the one that answers. Without `version_id` the most recently created version is deployed; after PATCH /v1/agents/{id}, pass the `version_id` it answered, because on an agent whose live version a campaign A/B test calls with, a setting saved afterwards is saved as a new version, which is then the newest. The previously deployed version becomes a draft, and the channels this agent is the only agent of switch to the new version (`deployed_channels` counts the agent's channels on it afterwards). Deploying a version that is already deployed changes nothing and answers `already_deployed: true`. The version goes live with its own instructions, language, identity and call opening, and with the agent's current automations, actions (follow-up emails, transfers, Cal.com) and knowledge and memory settings, which are saved in place on the live version: deploying never undoes them, as the dashboard's deploy never does.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nChanged in October 2026: a deploy through the API used to put a version live with the automations, actions and knowledge settings it was saved with, so it could undo the ones saved since. It now keeps the agent's current ones, as the dashboard's deploy does.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#deployAgent"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"version_id":{"description":"The version to deploy: the `version_id` PATCH /v1/agents/{id} answered (default: the newest version).","type":"string","format":"uuid"}}},"example":{"version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82"}}}},"responses":{"200":{"description":"The version is deployed.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.1.0","deployment_status":"deployed","deployed_channels":1,"published_at":"2026-10-02T02:21:38.917Z"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"deployAgent"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same). `not_found`: `version_id` is not a version of this agent, or the agent has no version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The version is in a campaign A/B test (`details.reason` `version_in_ab_test`), so it keeps its own settings, and deploying it would undo the ones saved since. Promote a variant to end the test first (in the dashboard: the campaign's Settings → A/B test), or deploy a newer version. Nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/automations":{"get":{"operationId":"getAgentAutomations","summary":"Get an agent's automations","description":"Returns what the agent does on its own: WhatsApp follow-ups after a customer goes quiet, appointment reminders (WhatsApp messages, a confirmation, a reminder call) and closing inactive conversations, each with the agent's channels it runs on, and what keeps going in a chat your team took over. `agent_id`, `agent_name` and `version_id` name the agent and the version the settings are on (null when it has none). `follow_ups.enabled_at` is when follow-ups were switched on (set by NuTalk): conversations quiet since before then are never followed up.\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentAutomations"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The agent's automations.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","follow_ups":{"enabled":true,"channel_ids":["9f19ef32-eaef-452c-bebc-14d9759cdb64"],"delays_minutes":[240,1440],"instructions":"Ask whether they still want the appointment.","quiet_hours_enabled":true,"quiet_start_hour":21,"quiet_end_hour":9,"enabled_at":"2026-10-23T18:04:11.000Z","follow_up_held_chats":false},"appointment_reminders":{"enabled":true,"whatsapp_channel_ids":["9f19ef32-eaef-452c-bebc-14d9759cdb64"],"voice_channel_ids":[],"offsets_minutes":[1440,120],"whatsapp_enabled":true,"confirmation_enabled":true,"voice_enabled":false,"voice_offset_minutes":240,"quiet_hours_enabled":true,"quiet_start_hour":21,"quiet_end_hour":9,"day_reminders_at_hour":false,"day_reminder_hour":9,"late_booking_reminders":false,"remind_held_chats":false},"auto_close":{"enabled":false,"channel_ids":[],"after_minutes":4320,"archive_enabled":true,"skip_human_takeover":true,"skip_customer_waiting":false},"campaign_messages":{"send_to_held_chats":false}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateAgentAutomations","summary":"Change an agent's automations","description":"Changes the settings sent, section by section, over what is saved at that moment; the rest stay as they are. Every channel must be one of the agent's active channels (WhatsApp for follow-ups, auto-close and WhatsApp reminders; voice for reminder calls), and a section switched on needs at least one. Follow-ups, reminders and reminder calls then go out to customers on their own.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nThe change applies at once on the agent's live version (its newest when none is live), with no deploy, and deploying another version later keeps it. A version a campaign A/B test calls with keeps its settings: the change is then saved as a new version, deployed in its place.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgentAutomations"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"follow_ups":{"description":"WhatsApp follow-ups after the customer goes quiet: only the settings to change.","type":"object","properties":{"enabled":{"description":"Follow up on its own.","type":"boolean"},"channel_ids":{"description":"The agent's active WhatsApp channels where it follows up (at least one while on).","type":"array","maxItems":50,"items":{"description":"A channel id.","type":"string","format":"uuid"}},"delays_minutes":{"description":"Minutes of silence before each follow-up, one per follow-up (at most 5), measured from the conversation's last message.","type":"array","minItems":1,"maxItems":5,"items":{"description":"Minutes.","type":"integer","minimum":5,"maximum":20160}},"instructions":{"description":"Guidance for the AI writing the follow-ups.","type":"string","maxLength":1000},"quiet_hours_enabled":{"description":"Send nothing during quiet hours.","type":"boolean"},"quiet_start_hour":{"description":"Local hour quiet hours start.","type":"integer","minimum":0,"maximum":23},"quiet_end_hour":{"description":"Local hour quiet hours end.","type":"integer","minimum":0,"maximum":23},"follow_up_held_chats":{"description":"Follow-ups still go out in a chat your team took over: the team, not the AI, answers there. Off by default: a take-over stops them. A person's own exception, set in the dashboard, wins.","type":"boolean"}}},"appointment_reminders":{"description":"Appointment reminders: only the settings to change.","type":"object","properties":{"enabled":{"description":"Remind people of their appointments.","type":"boolean"},"whatsapp_enabled":{"description":"Send WhatsApp reminders.","type":"boolean"},"whatsapp_channel_ids":{"description":"The agent's active WhatsApp channels that send reminders.","type":"array","maxItems":50,"items":{"description":"A channel id.","type":"string","format":"uuid"}},"offsets_minutes":{"description":"Minutes before the appointment for each WhatsApp reminder (at most 3).","type":"array","minItems":1,"maxItems":3,"items":{"description":"Minutes.","type":"integer","minimum":15,"maximum":10080}},"confirmation_enabled":{"description":"Ask the person to confirm, and record when they do.","type":"boolean"},"day_reminders_at_hour":{"description":"Send a reminder set in whole days (1 day, 2 days…) at `day_reminder_hour` of that day instead of at the appointment's hour.","type":"boolean"},"day_reminder_hour":{"description":"Local hour day reminders go out at.","type":"integer","minimum":0,"maximum":23},"late_booking_reminders":{"description":"An appointment booked after its day reminder's moment, for a later day, gets that reminder right away.","type":"boolean"},"remind_held_chats":{"description":"A WhatsApp reminder still goes out in a chat your team took over: the team, not the AI, answers there. Off by default: a take-over stops the reminder. A person's own exception, set in the dashboard, wins.","type":"boolean"},"voice_enabled":{"description":"Also place a reminder call.","type":"boolean"},"voice_channel_ids":{"description":"The agent's active voice channels that place reminder calls.","type":"array","maxItems":50,"items":{"description":"A channel id.","type":"string","format":"uuid"}},"voice_offset_minutes":{"description":"Minutes before the appointment for the reminder call.","type":"integer","minimum":15,"maximum":10080},"quiet_hours_enabled":{"description":"Hold reminders during quiet hours; due ones go out after.","type":"boolean"},"quiet_start_hour":{"description":"Local hour quiet hours start.","type":"integer","minimum":0,"maximum":23},"quiet_end_hour":{"description":"Local hour quiet hours end.","type":"integer","minimum":0,"maximum":23}}},"auto_close":{"description":"Closing inactive conversations: only the settings to change.","type":"object","properties":{"enabled":{"description":"Close inactive conversations.","type":"boolean"},"channel_ids":{"description":"The agent's active WhatsApp channels where it closes them (at least one while on).","type":"array","maxItems":50,"items":{"description":"A channel id.","type":"string","format":"uuid"}},"after_minutes":{"description":"Minutes without a message before a conversation is closed.","type":"integer","minimum":60,"maximum":43200},"archive_enabled":{"description":"Also archive it when closing.","type":"boolean"},"skip_human_takeover":{"description":"Never close a conversation a person took over.","type":"boolean"},"skip_customer_waiting":{"description":"Never close a conversation whose last message is the customer's.","type":"boolean"}}},"campaign_messages":{"description":"Campaign messages on the agent's lines in a chat your team took over: only the settings to change.","type":"object","properties":{"send_to_held_chats":{"description":"A campaign's WhatsApp template step still goes out in a chat your team took over; otherwise it waits until the chat is handed back. Off by default (until it is saved, it follows the agent's own appointment_reminders.remind_held_chats; for an agent whose reminders the clinic sets, the value it had before they moved there). A person's own exception, set in the dashboard, wins.","type":"boolean"}}}}},"example":{"follow_ups":{"enabled":true,"channel_ids":["9f19ef32-eaef-452c-bebc-14d9759cdb64"],"delays_minutes":[240,1440]}}}}},"responses":{"200":{"description":"The automations as saved; `saved_as_new_version` is true when a campaign A/B test kept the version and the change became a new one.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","follow_ups":{"enabled":true,"channel_ids":["9f19ef32-eaef-452c-bebc-14d9759cdb64"],"delays_minutes":[240,1440],"instructions":"Ask whether they still want the appointment.","quiet_hours_enabled":true,"quiet_start_hour":21,"quiet_end_hour":9,"enabled_at":"2026-10-23T18:04:11.000Z","follow_up_held_chats":false},"appointment_reminders":{"enabled":true,"whatsapp_channel_ids":["9f19ef32-eaef-452c-bebc-14d9759cdb64"],"voice_channel_ids":[],"offsets_minutes":[1440,120],"whatsapp_enabled":true,"confirmation_enabled":true,"voice_enabled":false,"voice_offset_minutes":240,"quiet_hours_enabled":true,"quiet_start_hour":21,"quiet_end_hour":9,"day_reminders_at_hour":false,"day_reminder_hour":9,"late_booking_reminders":false,"remind_held_chats":false},"auto_close":{"enabled":false,"channel_ids":[],"after_minutes":4320,"archive_enabled":true,"skip_human_takeover":true,"skip_customer_waiting":false},"campaign_messages":{"send_to_held_chats":false},"saved_as_new_version":false}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgentAutomations"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent has no version yet, or its automations changed while saving (try again).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/follow-up-emails":{"get":{"operationId":"getAgentFollowUpEmails","summary":"Get an agent's follow-up emails","description":"Returns the emails the agent may send the person it talks to when they ask for one (an invitation to an event, a link), and what sending them needs: `workspace_enabled` (follow-up emails are off until NuTalk switches them on for the workspace) and `sending_ready` (the workspace's Resend can send; null when it couldn't be read). `readable` is false when the saved follow-ups can't be read; the next change replaces them. `agent_id`, `agent_name` and `version_id` name the agent and the version the settings are on (null when it has none).\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentFollowUpEmails"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The agent's follow-up emails.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","enabled":true,"items":[{"key":"taller_gratuito","enabled":true,"name":"Invitación al taller gratuito","when":"When the person asks about the free workshop.","subject":"{{contact.first_name|Hola}}, tu lugar en el taller","body":"Te esperamos el martes. Aquí está el enlace para unirte.","link":null,"event":{"title":"Taller gratuito","weekday":2,"time":"19:00","timezone":"America/Mexico_City","duration_minutes":60,"join_url":"https://meet.example.com/taller"},"tag":"taller","reply_to":null}],"readable":true,"workspace_enabled":true,"sending_ready":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateAgentFollowUpEmails","summary":"Change an agent's follow-up emails","description":"Saves the agent's follow-up emails. `items` replaces the whole list, `enabled` switches them all; what isn't sent stays as saved. Each enabled follow-up becomes an action the agent may take when the person asks for it, emailing the address they confirm. Switching emails on needs the workspace switch NuTalk turns on and the workspace's Resend ready to send; switching them off always saves.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nThe change applies at once on the agent's live version (its newest when none is live), with no deploy, and deploying another version later keeps it. A version a campaign A/B test calls with keeps its settings: the change is then saved as a new version, deployed in its place.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgentFollowUpEmails"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"Switch every follow-up email on or off.","type":"boolean"},"items":{"description":"Every follow-up email (at most 5); replaces the list.","type":"array","maxItems":5,"items":{"description":"A follow-up email.","type":"object","properties":{"key":{"description":"The follow-up's key: the action the agent calls is `followup_<key>`, and it never changes once saved. Send an existing follow-up's key to keep it; a new one sent without a key gets one from its name.","type":"string"},"enabled":{"description":"This email's own switch (default on).","type":"boolean"},"name":{"description":"What the AI sees it as.","type":"string","maxLength":80},"when":{"description":"When the AI should send it.","type":"string","maxLength":400},"subject":{"description":"The subject. Contact variables such as `{{contact.first_name|there}}` are filled.","type":"string","maxLength":150},"body":{"description":"Plain text. Contact variables are filled.","type":"string","maxLength":2000},"link":{"description":"A link the email offers.","type":["object","null"],"properties":{"label":{"description":"Its label.","type":"string","maxLength":40},"url":{"description":"An https:// link.","type":"string","format":"uri"}},"required":["label","url"]},"event":{"description":"A weekly event the email invites to: its next session.","type":["object","null"],"properties":{"title":{"description":"Its title.","type":"string","maxLength":120},"weekday":{"description":"0 is Sunday, 6 Saturday.","type":"integer","minimum":0,"maximum":6},"time":{"description":"\"HH:MM\", 24-hour.","type":"string"},"timezone":{"description":"Its time zone, e.g. America/Mexico_City.","type":"string"},"duration_minutes":{"description":"Its length.","type":"integer","minimum":15,"maximum":240},"join_url":{"description":"The video call's https:// link, when known.","type":["string","null"],"format":"uri"}},"required":["title","weekday","time","timezone","duration_minutes"]},"tag":{"description":"The tag the contact gets.","type":["string","null"],"maxLength":60},"reply_to":{"description":"Where replies go (default: the workspace owner's address).","type":["string","null"],"format":"email"}},"required":["name","when","subject","body"]}}}},"example":{"enabled":true,"items":[{"key":"taller_gratuito","enabled":true,"name":"Invitación al taller gratuito","when":"When the person asks about the free workshop.","subject":"{{contact.first_name|Hola}}, tu lugar en el taller","body":"Te esperamos el martes. Aquí está el enlace para unirte.","link":null,"event":{"title":"Taller gratuito","weekday":2,"time":"19:00","timezone":"America/Mexico_City","duration_minutes":60,"join_url":"https://meet.example.com/taller"},"tag":"taller","reply_to":null}]}}}},"responses":{"200":{"description":"The follow-up emails as saved; `changed` is false when nothing differed.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","enabled":true,"items":[{"key":"taller_gratuito","enabled":true,"name":"Invitación al taller gratuito","when":"When the person asks about the free workshop.","subject":"{{contact.first_name|Hola}}, tu lugar en el taller","body":"Te esperamos el martes. Aquí está el enlace para unirte.","link":null,"event":{"title":"Taller gratuito","weekday":2,"time":"19:00","timezone":"America/Mexico_City","duration_minutes":60,"join_url":"https://meet.example.com/taller"},"tag":"taller","reply_to":null}],"readable":true,"changed":true}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgentFollowUpEmails"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key. `forbidden`: Follow-up emails are off for the workspace (`details.reason` `followup_emails_off`): NuTalk switches them on.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The workspace's Resend isn't ready (`details.reason` `resend_not_connected`), or the agent has no version yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/human-handoff":{"get":{"operationId":"getAgentHumanHandoff","summary":"Get an agent's human handoff","description":"Returns the agent's transfer to a person on calls: whether it may put a caller through to the workspace's AiBox handset, how many seconds it rings, the whisper the person hears before the caller, and when the agent should transfer. `handsets` lists the workspace's handsets (none: a transfer has nowhere to ring). `agent_id`, `agent_name` and `version_id` name the agent and the version the settings are on (null when it has none).\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentHumanHandoff"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The agent's human handoff.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","enabled":true,"ring_seconds":25,"whisper_enabled":true,"whisper_intro":"Llamada de un paciente que pide hablar con recepción.","trigger_hint":"When the caller asks for a person or has an emergency.","handsets":[{"id":"2b3c4d5e-6f7a-4b8c-9dae-1f2a3b4c5d6e","name":"Recepción","status":"active","registration_status":"registered"}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateAgentHumanHandoff","summary":"Change an agent's human handoff","description":"Changes the settings sent; the rest stay as saved. A text sent as null is cleared.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nThe change applies at once on the agent's live version (its newest when none is live), with no deploy, and deploying another version later keeps it. A version a campaign A/B test calls with keeps its settings: the change is then saved as a new version, deployed in its place.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgentHumanHandoff"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"Whether the agent may transfer a caller.","type":"boolean"},"ring_seconds":{"description":"How long the handset rings.","type":"integer","minimum":5,"maximum":90},"whisper_enabled":{"description":"Whether the person hears a short intro before the caller.","type":"boolean"},"whisper_intro":{"description":"What the person hears first.","type":["string","null"],"maxLength":300},"trigger_hint":{"description":"When the agent should transfer.","type":["string","null"],"maxLength":500}}},"example":{"enabled":true,"ring_seconds":30}}}},"responses":{"200":{"description":"The human handoff as saved, with the workspace's handsets.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","enabled":true,"ring_seconds":30,"whisper_enabled":true,"whisper_intro":null,"trigger_hint":null,"handsets":[]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgentHumanHandoff"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/pbx-transfer":{"get":{"operationId":"getAgentPbxTransfer","summary":"Get an agent's PBX transfers","description":"Returns the extensions of the workspace's PBX the agent may put a caller through to, each with a note on when, and `extensions`: the AI extensions to choose from (`is_own`: one the agent itself answers). `available` is false when the workspace has no PBX module. `agent_id`, `agent_name` and `version_id` name the agent and the version the settings are on (null when it has none).\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentPbxTransfer"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The agent's PBX transfers.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","available":true,"enabled":true,"targets":[{"extension_id":"1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d","note":"Billing questions."}],"extensions":[{"id":"1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d","system_name":"Oficina","number":"201","label":"Cobranza","description":null,"is_own":false}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateAgentPbxTransfer","summary":"Change an agent's PBX transfers","description":"Changes the switch and the extensions: `targets` replaces the list; what isn't sent stays as saved, except saved extensions that are no longer live, which are dropped. Only the workspace's live AI extensions can be targets.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nThe change applies at once on the agent's live version (its newest when none is live), with no deploy, and deploying another version later keeps it. A version a campaign A/B test calls with keeps its settings: the change is then saved as a new version, deployed in its place.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgentPbxTransfer"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"Whether the agent may transfer to them.","type":"boolean"},"targets":{"description":"The extensions it may transfer to (at most 20); replaces the list.","type":"array","maxItems":20,"items":{"description":"An extension.","type":"object","properties":{"extension_id":{"description":"An id from `extensions`.","type":"string","format":"uuid"},"note":{"description":"When to transfer there.","type":["string","null"],"maxLength":240}},"required":["extension_id"]}}}},"example":{"enabled":true,"targets":[{"extension_id":"1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d","note":"Billing questions."}]}}}},"responses":{"200":{"description":"The PBX transfers as saved.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","available":true,"enabled":true,"targets":[{"extension_id":"1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d","note":"Billing questions."}],"extensions":[]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgentPbxTransfer"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key. `forbidden`: The workspace has no PBX module (NuTalk enables it for corporate plans).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/context-settings":{"get":{"operationId":"getAgentContextSettings","summary":"Get an agent's knowledge and memory settings","description":"Returns whether the agent answers from its knowledge sources and what it remembers of each person it talks to. `agent_id`, `agent_name` and `version_id` name the agent and the version the settings are on (null when it has none).\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentContextSettings"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The agent's knowledge and memory settings.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","knowledge_enabled":true,"inject_profile":true,"inject_episodes":true,"recall_tool_enabled":true,"restart_mode":"smart","memory_scope":"channels","memory_channel_ids":["6e1b3a5c-7d9f-4b2e-8a4c-6e8a0c2e4b17"]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateAgentContextSettings","summary":"Change an agent's knowledge and memory settings","description":"Changes the settings sent; the rest stay as saved. `memory_channel_ids` must be channels of the workspace.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nThe change applies at once on the agent's live version (its newest when none is live), with no deploy, and deploying another version later keeps it. A version a campaign A/B test calls with keeps its settings: the change is then saved as a new version, deployed in its place.\n\nThe technical values (passages per answer, the quiet period that ends a conversation's summary, the search server) are set by NuTalk and can't be sent.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgentContextSettings"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"knowledge_enabled":{"description":"Answer from the agent's knowledge sources.","type":"boolean"},"inject_profile":{"description":"Remember what each person told it before (their profile).","type":"boolean"},"inject_episodes":{"description":"Remember summaries of each person's past conversations.","type":"boolean"},"recall_tool_enabled":{"description":"Let the agent search a person's past conversations when it needs to.","type":"boolean"},"restart_mode":{"description":"How a returning person's conversation restarts: `smart` (the agent decides), `resume` (it carries on) or `fresh`.","type":"string","enum":["smart","resume","fresh"]},"memory_scope":{"description":"Who shares what the agent remembers of a person: `channel` (the conversation's channel), `channels` (it and `memory_channel_ids`), `agent` (every channel the agent runs on) or `workspace`.","type":"string","enum":["channel","channels","agent","workspace"]},"memory_channel_ids":{"description":"The workspace's channels pooled with `memory_scope` `channels`.","type":"array","maxItems":50,"items":{"description":"A channel id.","type":"string","format":"uuid"}}}},"example":{"memory_scope":"channels","memory_channel_ids":["6e1b3a5c-7d9f-4b2e-8a4c-6e8a0c2e4b17"]}}}},"responses":{"200":{"description":"The knowledge and memory settings as saved.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","knowledge_enabled":true,"inject_profile":true,"inject_episodes":true,"recall_tool_enabled":true,"restart_mode":"smart","memory_scope":"channels","memory_channel_ids":["6e1b3a5c-7d9f-4b2e-8a4c-6e8a0c2e4b17"]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgentContextSettings"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent has no version to save the settings on yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/calcom":{"get":{"operationId":"getAgentCalcom","summary":"Get an agent's Cal.com booking","description":"Returns how the agent books with Cal.com: whether it is on, the event type, its length and time zone, and `connected`: whether the workspace's Cal.com account is connected (its credentials are never returned). `agent_id`, `agent_name` and `version_id` name the agent and the version the settings are on (null when it has none).\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentCalcom"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The agent's Cal.com booking.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","enabled":true,"event_type_slug":"consulta-30","event_type_id":1234567,"duration_minutes":30,"timezone":"America/Monterrey","connected":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateAgentCalcom","summary":"Change an agent's Cal.com booking","description":"Changes the settings sent; the rest stay as saved. While booking is on, the event type is checked with Cal.com. Connecting or disconnecting the workspace's Cal.com account is done in the dashboard.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nThe change applies at once on the agent's live version (its newest when none is live), with no deploy, and deploying another version later keeps it. A version a campaign A/B test calls with keeps its settings: the change is then saved as a new version, deployed in its place.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgentCalcom"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"Book with Cal.com. Switching it on checks the event type with Cal.com, and an agent books one way only: when another booking method is chosen for it, it is refused.","type":"boolean"},"event_type_id":{"description":"The event type's id (`GET /v1/agents/{id}/calcom/event-types`), sent with its `event_type_slug`: the agent books by the id.","type":"integer","minimum":1},"event_type_slug":{"description":"The event type's slug, sent with its `event_type_id`.","type":"string","maxLength":200},"duration_minutes":{"description":"The meeting's length, when the event type allows several; null clears it.","type":["integer","null"],"minimum":5,"maximum":480},"timezone":{"description":"The time zone it books in, e.g. America/Monterrey.","type":"string","maxLength":64}}},"example":{"enabled":true,"event_type_id":1234567,"event_type_slug":"consulta-30","timezone":"America/Monterrey"}}}},"responses":{"200":{"description":"The agent's Cal.com booking as saved.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","enabled":true,"event_type_slug":"consulta-30","event_type_id":1234567,"duration_minutes":30,"timezone":"America/Monterrey","connected":true}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgentCalcom"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The workspace's Cal.com account isn't connected (`details.reason` `calcom_not_connected`): connect it in the dashboard first. `conflict`: Another booking method is chosen for the agent (`details.reason` `booking_method_conflict`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error`: Cal.com refused the event type (`details.reason` `calcom_check_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/calcom/event-types":{"get":{"operationId":"listAgentCalcomEventTypes","summary":"List the Cal.com event types","description":"Lists the event types of the workspace's Cal.com account (asked of Cal.com with the workspace's connection), to choose the one the agent books.\n\nRequires the `agents:read` scope.\n\nRead as the member who created the key, with a role that edits agents (owner, admin or builder).","tags":["Agents"],"x-required-scope":"agents:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#listAgentCalcomEventTypes"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The event types.","content":{"application/json":{"example":{"success":true,"data":[{"id":1234567,"slug":"consulta-30","title":"Consulta (30 min)","length_minutes":30}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The workspace's Cal.com account isn't connected (`details.reason` `calcom_not_connected`): connect it in the dashboard first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/restore":{"post":{"operationId":"restoreAgent","summary":"Restore an archived agent","description":"Brings back an archived agent (`GET /v1/agents?status=archived` lists them). It is active again when one of its versions is still deployed, a draft otherwise, and it comes back on no channel: put it on one with `PUT /v1/channels/{id}/agent`. An agent that isn't archived is left as it is (`restored: false`).\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#restoreAgent"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The agent and its status.","content":{"application/json":{"example":{"success":true,"data":{"id":"550e8400-e29b-41d4-a716-446655440000","restored":true,"status":"active"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"restoreAgent"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/booking-method":{"get":{"operationId":"getAgentBookingMethod","summary":"Get an agent's booking method","description":"How the agent books: one method per agent (`none`, `clinic`, `business`, `calcom` or `api_action`, one of its REST write actions). `method` is null when several are in use and none was chosen. `available` says which methods the workspace can use now and why not; `lines` are the agent's channels and the scheduling mode each runs.\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentBookingMethod"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The booking method.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":null,"method":"business","chosen":true,"in_use":["business"],"proposed":"business","action":null,"available":{"clinic":{"available":false,"reason":"module_missing"},"business":{"available":true,"reason":null},"calcom":{"available":false,"reason":"calcom_not_connected"},"api_action":{"available":false,"reason":"no_write_actions"}},"lines":[{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Sales line","type":"voice","scheduling_mode":"business"}],"write_actions":[],"business":{"meeting_type_ids":["4f2b6a1c-8d3e-4a5b-9c7d-1e2f3a4b5c6d"],"default_meeting_type_id":null}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"setAgentBookingMethod","summary":"Change an agent's booking method","description":"Sets how the agent books, as its Booking card does: it records the method, makes the agent's live versions follow it and points the agent's channels at it, in one transaction. `business` takes `meeting_type_ids` (the agent's meeting types; people for each are set with business-booking), `api_action` the REST write action that books.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nRestaurant reservations can't be an agent's booking method yet.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#setAgentBookingMethod"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"method":{"description":"The booking method.","type":"string","enum":["none","clinic","business","calcom","api_action"]},"action":{"description":"For `api_action`: the name of one of the agent's REST write actions.","type":"string"},"meeting_type_ids":{"description":"For `business`: the meeting types the agent offers.","type":"array","minItems":1,"maxItems":50,"items":{"description":"A meeting type id.","type":"string","format":"uuid"}},"default_meeting_type_id":{"description":"For `business`, instead of a list: the one meeting type an older agent books.","type":"string","format":"uuid"}},"required":["method"]},"example":{"method":"business","meeting_type_ids":["4f2b6a1c-8d3e-4a5b-9c7d-1e2f3a4b5c6d","5a3c7b2d-9e4f-4b6c-8d8e-2f3a4b5c6d7e"]}}}},"responses":{"200":{"description":"The booking method as saved.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":null,"method":"business","chosen":true,"in_use":["business"],"proposed":"business","action":null,"available":{"clinic":{"available":false,"reason":"module_missing"},"business":{"available":true,"reason":null},"calcom":{"available":false,"reason":"calcom_not_connected"},"api_action":{"available":false,"reason":"no_write_actions"}},"lines":[{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Sales line","type":"voice","scheduling_mode":"business"}],"write_actions":[],"business":{"meeting_type_ids":["4f2b6a1c-8d3e-4a5b-9c7d-1e2f3a4b5c6d"],"default_meeting_type_id":null}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setAgentBookingMethod"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The method isn't available for the workspace (its module is off, Cal.com isn't connected, the REST actions are off), a meeting type isn't the workspace's (`details.reason` `meeting_type_not_available`), or the action isn't one of the agent's write actions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key lacks `channels:write` (the change points the agent's channels at the method), or the member who created it can't edit agents, or it is a reseller key; or the agent answers a Zoom Phone line and that member isn't an owner or admin (`details.reason` `ZOOM_MANAGERS_ONLY`), since the method is copied onto that line too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: A campaign A/B test calls with the agent's live version (`details.reason` `live_version_in_ab_test`): promote a variant first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/clinic-booking":{"get":{"operationId":"getAgentClinicBooking","summary":"Get an agent's clinic booking","description":"How the agent books clinic appointments: which doctors, rooms and services it offers, its defaults, what it asks the patient, what it may do with an appointment and its emergency guidance. Empty lists mean all of them.\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentClinicBooking"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The clinic booking.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":null,"enabled":true,"timezone":"America/Monterrey","scopes":{"consult_services":true,"consult_deals":true,"check_availability":true,"book_appointment":true,"lookup_appointments":true,"reschedule_appointment":true,"cancel_appointment":false,"confirm_appointment":true},"allowed_doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"],"allowed_room_ids":[],"allowed_service_ids":["c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4"],"default_doctor_id":null,"default_room_id":null,"default_service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","required_patient_fields":["name","phone"],"patient_record_ask":"after_booking","patient_arrival_offset_minutes":10,"confirmation_instructions":"Bring your ID and arrive 10 minutes early.","fallback_message":null,"emergency_guidance_enabled":true,"emergency_contact":"911","emergency_message_es":null,"emergency_message_en":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateAgentClinicBooking","summary":"Change an agent's clinic booking","description":"Changes the settings sent; the rest stay as saved. Doctors, rooms and services must be the workspace's active ones (GET /v1/clinic/doctors, rooms and services).\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nThe change applies at once on the agent's live version (its newest when none is live), with no deploy. A version a campaign A/B test calls with keeps its settings: the change is then saved as a new version, deployed in its place.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgentClinicBooking"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"The agent books clinic appointments.","type":"boolean"},"timezone":{"description":"IANA timezone; null uses the clinic's.","type":["string","null"]},"scopes":{"description":"What the agent may do, each true or false; only the ones sent change. Booking or rescheduling turns availability on.","type":"object","properties":{"consult_services":{"description":"consult services.","type":"boolean"},"consult_deals":{"description":"consult deals.","type":"boolean"},"check_availability":{"description":"check availability.","type":"boolean"},"book_appointment":{"description":"book appointment.","type":"boolean"},"lookup_appointments":{"description":"lookup appointments.","type":"boolean"},"reschedule_appointment":{"description":"reschedule appointment.","type":"boolean"},"cancel_appointment":{"description":"cancel appointment.","type":"boolean"},"confirm_appointment":{"description":"confirm appointment.","type":"boolean"}}},"allowed_doctor_ids":{"description":"The doctors it offers ([]: all of them).","type":"array","items":{"description":"A doctor id.","type":"string","format":"uuid"}},"allowed_room_ids":{"description":"The rooms it books ([]: all of them).","type":"array","items":{"description":"A room id.","type":"string","format":"uuid"}},"allowed_service_ids":{"description":"The services it offers ([]: all of them).","type":"array","items":{"description":"A service id.","type":"string","format":"uuid"}},"default_doctor_id":{"description":"The doctor booked when the patient has no preference.","type":["string","null"],"format":"uuid"},"default_room_id":{"description":"The room booked by default.","type":["string","null"],"format":"uuid"},"default_service_id":{"description":"The service booked when none is named.","type":["string","null"],"format":"uuid"},"required_patient_fields":{"description":"What it asks the patient before booking.","type":"array","items":{"description":"A field.","type":"string","enum":["name","phone","email"]}},"patient_record_ask":{"description":"When it asks the clinic's patient record: `after_booking` (the default), `before_booking` or `never`. On WhatsApp lines. It never blocks a booking.","type":"string","enum":["after_booking","before_booking","never"]},"patient_arrival_offset_minutes":{"description":"Minutes before the appointment the patient is asked to arrive.","type":"integer","minimum":0,"maximum":240},"confirmation_instructions":{"description":"What it tells the patient after booking.","type":["string","null"],"maxLength":4000},"fallback_message":{"description":"What it says when it can't book.","type":["string","null"],"maxLength":4000},"emergency_guidance_enabled":{"description":"It tells a patient describing an emergency to get urgent help instead of booking.","type":"boolean"},"emergency_contact":{"description":"The number or place it gives in an emergency.","type":["string","null"],"maxLength":4000},"emergency_message_es":{"description":"Its emergency message in Spanish.","type":["string","null"],"maxLength":4000},"emergency_message_en":{"description":"Its emergency message in English.","type":["string","null"],"maxLength":4000}}},"example":{"allowed_service_ids":["c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4"],"required_patient_fields":["name","phone","email"],"scopes":{"cancel_appointment":false}}}}},"responses":{"200":{"description":"The clinic booking as saved.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":null,"enabled":true,"timezone":"America/Monterrey","scopes":{"consult_services":true,"consult_deals":true,"check_availability":true,"book_appointment":true,"lookup_appointments":true,"reschedule_appointment":true,"cancel_appointment":false,"confirm_appointment":true},"allowed_doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"],"allowed_room_ids":[],"allowed_service_ids":["c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4"],"default_doctor_id":null,"default_room_id":null,"default_service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","required_patient_fields":["name","phone"],"patient_record_ask":"after_booking","patient_arrival_offset_minutes":10,"confirmation_instructions":"Bring your ID and arrive 10 minutes early.","fallback_message":null,"emergency_guidance_enabled":true,"emergency_contact":"911","emergency_message_es":null,"emergency_message_en":null}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgentClinicBooking"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: Another booking method is chosen for the agent (`details.reason` `booking_method_conflict`): change the booking method first. `conflict`: A room named was removed meanwhile (`details.reason` `room_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/business-booking":{"get":{"operationId":"getAgentBusinessBooking","summary":"Get an agent's business booking","description":"How the agent books business meetings: the meeting types it offers, each with the people who take it for this agent (null: everyone who takes it), the one it books without asking, HubSpot owner routing and what it may do with a booked meeting. `meeting_types` is null for an agent that books its one default meeting type; `can_book` says whether its saved settings can book on its channels.\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentBusinessBooking"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The business booking.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":null,"module_enabled":true,"enabled":true,"check_availability":true,"book_appointment":true,"lookup_appointments":true,"reschedule_appointment":true,"cancel_appointment":false,"meeting_types":[{"id":"4f2b6a1c-8d3e-4a5b-9c7d-1e2f3a4b5c6d","host_ids":null},{"id":"5a3c7b2d-9e4f-4b6c-8d8e-2f3a4b5c6d7e","host_ids":["6b4d8c3e-0f5a-4c7d-9e9f-3a4b5c6d7e8f"]}],"default_meeting_type_id":"4f2b6a1c-8d3e-4a5b-9c7d-1e2f3a4b5c6d","default_host_id":null,"route_by_hubspot_owner":false,"timezone":"America/Monterrey","can_book":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateAgentBusinessBooking","summary":"Change an agent's business booking","description":"Changes the settings sent; the rest stay as saved. `meeting_types` replaces the list (at least one; to stop booking, set the booking method to `none`). With meeting types, who takes each one is set per type, so `default_host_id` can't change.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nThe change applies at once on the agent's live version (its newest when none is live), with no deploy. A version a campaign A/B test calls with keeps its settings: the change is then saved as a new version, deployed in its place.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgentBusinessBooking"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"The agent books business meetings.","type":"boolean"},"check_availability":{"description":"It may look up free times.","type":"boolean"},"book_appointment":{"description":"It may book a meeting.","type":"boolean"},"lookup_appointments":{"description":"It may find the person's meetings.","type":"boolean"},"reschedule_appointment":{"description":"It may move a meeting.","type":"boolean"},"cancel_appointment":{"description":"It may cancel a meeting.","type":"boolean"},"meeting_types":{"description":"The meeting types it offers, from GET /v1/business/meeting-types; replaces the list.","type":"array","minItems":1,"maxItems":50,"items":{"description":"A meeting type.","type":"object","properties":{"id":{"description":"The meeting type.","type":"string","format":"uuid"},"host_ids":{"description":"The people who take it for this agent; null: everyone who takes it.","type":["array","null"],"items":{"description":"A host id.","type":"string","format":"uuid"}}},"required":["id"]}},"default_meeting_type_id":{"description":"The meeting type it books without asking; one of its meeting types.","type":["string","null"],"format":"uuid"},"default_host_id":{"description":"For an agent without meeting types (one default type): the host it books with.","type":["string","null"],"format":"uuid"},"route_by_hubspot_owner":{"description":"A lead with a HubSpot owner meets that owner when they take the meeting type.","type":"boolean"},"timezone":{"description":"IANA timezone; null uses the workspace's.","type":["string","null"]}}},"example":{"meeting_types":[{"id":"4f2b6a1c-8d3e-4a5b-9c7d-1e2f3a4b5c6d","host_ids":null},{"id":"5a3c7b2d-9e4f-4b6c-8d8e-2f3a4b5c6d7e","host_ids":["6b4d8c3e-0f5a-4c7d-9e9f-3a4b5c6d7e8f"]}],"default_meeting_type_id":"4f2b6a1c-8d3e-4a5b-9c7d-1e2f3a4b5c6d"}}}},"responses":{"200":{"description":"The business booking as saved.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":null,"module_enabled":true,"enabled":true,"check_availability":true,"book_appointment":true,"lookup_appointments":true,"reschedule_appointment":true,"cancel_appointment":false,"meeting_types":[{"id":"4f2b6a1c-8d3e-4a5b-9c7d-1e2f3a4b5c6d","host_ids":null},{"id":"5a3c7b2d-9e4f-4b6c-8d8e-2f3a4b5c6d7e","host_ids":["6b4d8c3e-0f5a-4c7d-9e9f-3a4b5c6d7e8f"]}],"default_meeting_type_id":"4f2b6a1c-8d3e-4a5b-9c7d-1e2f3a4b5c6d","default_host_id":null,"route_by_hubspot_owner":false,"timezone":"America/Monterrey","can_book":true}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgentBusinessBooking"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The Business module is off, a meeting type or person isn't available (`details.reason` says which rule), the default isn't one of the meeting types (`default_not_listed`), or the default host can't change (`default_host_per_meeting_type`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: Another booking method is chosen for the agent (`details.reason` `booking_method_conflict`): change the booking method first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/rest-connector":{"get":{"operationId":"getAgentRestConnector","summary":"Get an agent's REST connector","description":"The REST actions the agent may call on your API during a conversation: the base URL, how it signs in (`has_token` and `has_password` say whether one is saved, never its value), the actions and the instructions for using them. Each write action has its `risk` (who says yes before NuTalk makes the change) and `channels` lists the agent's channels with how the customer confirms on each: `buttons` on WhatsApp's Cloud API, `code` in the other chats, and `conversation` on a call (the agent asks in the conversation) until calls ask for a key (`dtmf`).\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentRestConnector"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The REST connector.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":null,"configured":true,"enabled":true,"base_url":"https://api.example.com/v2","auth_type":"bearer","auth_header":"Authorization","auth_username":null,"has_token":true,"has_password":false,"actions":[{"name":"order_status","description":"Look up an order when the customer gives its number.","label":null,"method":"GET","path":"/orders/{order_number}","mode":"read","required_params":["order_number"],"response_hint":"status and delivery_date","timeout_ms":10000,"risk":null,"high_mode":null,"confirm_off_channels":[]},{"name":"book_visit","description":"Book a visit once the customer picks a time.","label":"Book your visit","method":"POST","path":"/visits","mode":"write","required_params":["slot_start","email"],"response_hint":null,"timeout_ms":10000,"risk":"medium","high_mode":null,"confirm_off_channels":[]}],"instructions":"Ask for the order number before looking it up.","channels":[{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Sales","kind":"whatsapp","confirm_via":"buttons"}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateAgentRestConnector","summary":"Change an agent's REST connector","description":"Changes the settings sent; the rest stay as saved. The base URL must be a public HTTPS address. Tokens and passwords are added in NuTalk only (the agent's Actions → REST API), never through the API: a change to the base URL (another path on the same host too, a trailing slash aside) or to how it signs in clears the saved ones (`secrets_cleared: true`), so they never follow it there. Such a change can't switch the connector on: send `enabled: false` with it, add the secret in NuTalk, then switch it on.\n\nRequires the `agents:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key: the key needs `agents:write`, and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nThe agent calls your API with what customers tell it: confirm the URL and the actions with the user first.\n\nOnly an owner's or admin's key may add or change an action that can change data (mode write, or any method but GET), the base URL or how it signs in; each such change is told to the workspace's owners.\n\nA key acts at most as an admin: risk `low` and a channel without the customer's confirmation are set by an owner in NuTalk, and a save that leaves an action's risk fields out keeps them.","tags":["Agents"],"x-required-scope":"agents:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#updateAgentRestConnector"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"The agent may call the actions.","type":"boolean"},"base_url":{"description":"A public HTTPS URL, with no credentials, query or fragment.","type":["string","null"],"maxLength":500},"auth_type":{"description":"How the connector signs in. The token or password is added in NuTalk.","type":"string","enum":["none","bearer","api_key_header","basic","custom_header"]},"auth_header":{"description":"The header for api_key_header and custom_header.","type":"string","maxLength":80},"auth_username":{"description":"The username for basic.","type":["string","null"],"maxLength":200},"actions":{"description":"The actions the agent may call; replaces the list.","type":"array","maxItems":20,"items":{"description":"An action.","type":"object","properties":{"name":{"description":"2-64 lowercase letters, digits or _, starting with a letter: how the agent calls it. Names starting with followup_ are the agent's follow-up emails.","type":"string"},"description":{"description":"When the agent should call it.","type":"string","maxLength":500},"method":{"description":"The HTTP method.","type":"string","enum":["GET","POST","PUT","PATCH"],"default":"POST"},"path":{"description":"The path after the base URL, starting with /.","type":"string","maxLength":300},"mode":{"description":"read looks something up; write changes something (and can be the booking method).","type":"string","enum":["read","write"],"default":"read"},"required_params":{"description":"The parameters the agent must collect before calling it.","type":"array","maxItems":12,"items":{"description":"A parameter name.","type":"string"}},"response_hint":{"description":"How to read the answer.","type":["string","null"],"maxLength":500},"timeout_ms":{"description":"How long the agent waits.","type":"integer","default":10000,"minimum":1000,"maximum":15000},"label":{"description":"What the customer reads as the action's name when asked to confirm it. Left out, the saved one stays; null clears it.","type":["string","null"],"maxLength":60},"risk":{"description":"Write actions only: `medium` (the default) the customer confirms each one; `high` the agent never does it on its own (`high_mode`); `low` never asks, and only an owner sets it, in NuTalk. Left out or null (with `high_mode` and `confirm_off_channels`), the saved risk stays.","type":["string","null"],"enum":["low","medium","high",null]},"high_mode":{"description":"For risk `high`: `team` (the default) an owner or admin approves each one in NuTalk; `blocked` the agent may not use it.","type":["string","null"],"enum":["team","blocked",null]},"confirm_off_channels":{"description":"For risk `medium`: the agent's channels where the customer doesn't confirm it. Only an owner turns one off, in NuTalk; any key may turn one back on by leaving it out.","type":"array","maxItems":50,"items":{"description":"One of the agent's `channels`.","type":"string","format":"uuid"}}},"required":["name","description","path"]}},"instructions":{"description":"How the agent should use the actions.","type":["string","null"],"maxLength":4000}}},"example":{"base_url":"https://api.example.com/v2","actions":[{"name":"order_status","description":"Look up an order when the customer gives its number.","method":"GET","path":"/orders/{order_number}","mode":"read","required_params":["order_number"]}]}}}},"responses":{"200":{"description":"The REST connector as saved, with `secrets_cleared`.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Sofía","version_id":null,"configured":true,"enabled":true,"base_url":"https://api.example.com/v2","auth_type":"bearer","auth_header":"Authorization","auth_username":null,"has_token":true,"has_password":false,"actions":[{"name":"order_status","description":"Look up an order when the customer gives its number.","label":null,"method":"GET","path":"/orders/{order_number}","mode":"read","required_params":["order_number"],"response_hint":"status and delivery_date","timeout_ms":10000,"risk":null,"high_mode":null,"confirm_off_channels":[]},{"name":"book_visit","description":"Book a visit once the customer picks a time.","label":"Book your visit","method":"POST","path":"/visits","mode":"write","required_params":["slot_start","email"],"response_hint":null,"timeout_ms":10000,"risk":"medium","high_mode":null,"confirm_off_channels":[]}],"instructions":"Ask for the order number before looking it up.","channels":[{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Sales","kind":"whatsapp","confirm_via":"buttons"}],"secrets_cleared":false}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateAgentRestConnector"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The base URL isn't public HTTPS, an action is invalid, a name is reserved (`reserved_action_name`), the connector needs a secret NuTalk doesn't have (`secret_required`), or switching it on lacks a URL or an action.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key. `forbidden`: The change adds or changes an action that can change data (mode write, or any method but GET), moves the base URL or changes how it signs in, and the key's creator isn't an owner or admin (`owner_or_admin_required`), or it sets an action's risk to `low` or turns the customer's confirmation off on a channel, which only an owner does, in NuTalk (`owner_required`): nothing was saved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: Another booking method is chosen for the agent (`details.reason` `booking_method_conflict`): change the booking method first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/versions/{version_id}":{"get":{"operationId":"getAgentVersion","summary":"Get one version of an agent","description":"One version of the agent, deployed or a draft, with its instructions and the settings PATCH /v1/agents/{id} writes (language, identity, purpose, call opening): what a draft says before it is deployed. GET /v1/agents/{id} lists the versions.\n\nRequires the `agents:read` scope.","tags":["Agents"],"x-required-scope":"agents:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agents#getAgentVersion"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"version_id","in":"path","required":true,"description":"The version id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The version.","content":{"application/json":{"example":{"success":true,"data":{"id":"c5c0810c-4614-4f0d-927d-62e587dad45e","agent_id":"550e8400-e29b-41d4-a716-446655440000","version_label":"v1.5","deployment_status":"draft","published_at":null,"created_at":"2026-10-26T15:00:00.000Z","instructions":"You are Sofía, the clinic's receptionist...","language":"es-MX","identity_name":"Sofía","purpose":"Book first consultations","greeting":"¡Hola! Soy Sofía.","say_greeting_exactly":false}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent or version with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/calls":{"post":{"operationId":"createCall","summary":"Place an outbound call","description":"Dials `to` from a voice channel, with the agent deployed on that channel. Calls go through NuTalk's call queue, which applies your plan's simultaneous-call and hourly limits and your credit balance. The request waits up to about 20 seconds for the queue: when the call has been placed you get 201 with its id, otherwise 202 with the queue job id (the call is still placed when a line frees up).\n\nRequires the `calls:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nRetry a failed or timed-out request with the same `Idempotency-Key`: the first response is replayed and the number is not dialed twice.\n\nA number on the do-not-call list is refused before anything is dialed or charged, as in a campaign. The match is by the person, not by how the number is written: any spelling of the number, and any other number the contact has. To call the person again, lift the opt-out first (PATCH /v1/contacts/{id} with `do_not_contact: false`). A test call is checked the same way once its number passed the test-number rule.\n\nThe list is looked at twice: when the request arrives, and again right before the dial, with the number as the line will dial it. So a call that waited in the queue is not placed to a person who opted out meanwhile. Refused at the dial while this request still waits, the answer is the same 409 `do_not_contact`; refused later, the job ends `dead` with `error` `do_not_contact` (GET /v1/call-jobs/{queue_job_id}) and no call is created.","tags":["Calls"],"x-required-scope":"calls:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#createCall"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel_id":{"description":"A voice channel (phone number) of your workspace with a deployed agent. See GET /v1/channels?type=voice.","type":"string","format":"uuid"},"to":{"description":"Number to call, in E.164 with the country code, e.g. +14155551234. A number sent without its country code is dialed as the channel's line reads it (a Zoom Phone line reads ten digits as a Mexican number; another line may read them differently or not dial them), so always send the whole number.","type":"string","maxLength":20},"caller_id":{"description":"Caller ID to present, when the channel's carrier allows it: one of your workspace's numbers (this channel's number, or a number your workspace owns or is assigned).","type":"string","maxLength":20},"notes":{"description":"Context for this call, kept as a note on the call for your team. `GET /v1/calls/{id}` does not return it.","type":"string","maxLength":1000},"status_callback_url":{"description":"Public URL that receives one unsigned POST with the call's outcome when it ends (no retries). Must not be localhost or a private address. For signed, retried events use webhooks instead.","type":"string","format":"uri"},"test_call":{"description":"Places a free test call: `to` must be one of the workspace's verified test numbers (at most 4, in Mexico, the US or Canada, added in the dashboard). It rings from NuTalk's test line, lasts at most 5 minutes and costs no credits; each workspace gets 10 a day, unanswered calls included.","type":"boolean"}},"required":["channel_id","to"]},"example":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","to":"+14155551234","notes":"Follow-up call for demo scheduling","status_callback_url":"https://your-app.example.com/webhooks/call-status"}}}},"responses":{"201":{"description":"The call was placed.","content":{"application/json":{"example":{"success":true,"data":{"id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","to":"+14155551234","from":"+525512345678","direction":"outbound","status":"queued","queued":true,"queue_job_id":"9718d2fe-ecf2-4630-82c6-599dfcc0f9fd","queue_status":"completed"}}}}},"202":{"description":"Accepted and queued; it has not been dialed yet (all lines busy, or the queue worker has not answered within ~20 s). Follow it with GET /v1/call-jobs/{queue_job_id}: once the job completes, its `call_id` is the call.","content":{"application/json":{"example":{"success":true,"data":{"queued":true,"queue_job_id":"9718d2fe-ecf2-4630-82c6-599dfcc0f9fd","queue_status":"queued"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `channel_id` is not a voice channel, or `status_callback_url` is localhost, a private address or not http(s). `validation_error`: `caller_id` is not one of your workspace's numbers (`error.details.field`: caller_id). `validation_error`: `test_call` is true and `to` is not a valid mobile or landline number in Mexico, the US or Canada (`error.details.reason`: TEST_CALL_INVALID_NUMBER, TEST_CALL_COUNTRY_NOT_SUPPORTED or TEST_CALL_NUMBER_NOT_CALLABLE).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: `test_call` is true and `to` is not one of the workspace's verified test numbers (`error.details.reason`: TEST_CALL_NUMBER_NOT_ALLOWED).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: No agent with a deployed version is on this channel. `conflict`: `test_call` is true and the workspace used today's free test calls (`error.details.reason`: TEST_CALL_DAILY_LIMIT, with `error.details.quota`; a channel on the workspace's own HT813 line has no daily limit), test calls are paused (TEST_CALL_PAUSED, also for a workspace set to 0 a day), or `to` is the channel's own number (TEST_CALL_SAME_AS_CALLER_ID). `workspace_suspended`: The workspace is suspended and the call would be dialed directly (a test call to NuTalk's own test line). A call that goes through the queue waits there until the workspace is reactivated. `do_not_contact`: `to` is on the do-not-call list: the person opted out, or the number is on the workspace's exclusion list. Nothing was dialed or charged. Do not retry it. A key that also holds `contacts:read` is told who and since when (`error.details.contact_id`, `error.details.since`); a key without it is told only that the number may not be called. A number sent without its country code is also refused when a number a line could dial for it is on the list (the ten digits as a Mexican number, or as a US or Canadian one): `error.details.dialed_as` is that number, and no contact is named for any key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"description":"The queue gave up on the call after its attempts (`queue_status: \"dead\"`, the reason in `error`)."},"503":{"description":"`service_unavailable`: `test_call` is true and test calls are not available yet (`error.details.reason`: TEST_CALL_NOT_AVAILABLE). `service_unavailable`: The do-not-call list could not be checked, so no call was placed. Retry with the same `Idempotency-Key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listCalls","summary":"List calls","description":"Lists inbound and outbound calls, newest first (by start time). A campaign's call names its enrollment (`campaign_enrollment_id`; null for other calls). Transcripts and summaries are on GET /v1/calls/{id}.\n\nRequires the `calls:read` scope.\n\n`failure_reason` says why an outbound call reached nobody, and `failure_detail` says it in a sentence (English), e.g. `declined` with \"The person or their phone company rejected the call.\". Both are null for a call that connected, an inbound call and a call still going. `failure_reason` is one of: `invalid_number`, `not_in_service`, `unreachable`, `busy`, `declined`, `blocked_spam`, `no_answer`, `voicemail`, `hung_up_right_away`, `answered_no_speech`, `ai_not_connected`, `caller_id_not_allowed`, `destination_not_allowed`, `needs_country_code`, `network_busy`, `line_in_use`, `no_credits`, `do_not_contact`, `not_interested`, `interested`, `callback`, `canceled`, `system_error`, `failed`. It reads what the carrier said (the dial's refusal, the hangup's SIP code), so it can be more precise than `call_outcome`: a declined call keeps `call_outcome: busy`. `failed` means nothing more specific is known; its `failure_detail` then names the code the carrier gave, when it gave one.","tags":["Calls"],"x-required-scope":"calls:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#listCalls"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only calls in this status. `in-progress` and `no-answer` are accepted as aliases.","schema":{"type":"string","enum":["queued","ringing","in_progress","completed","failed","canceled","voicemail","no_answer","busy","rejected"]}},{"name":"direction","in":"query","required":false,"description":"Only inbound or only outbound calls.","schema":{"type":"string","enum":["inbound","outbound"]}},{"name":"from","in":"query","required":false,"description":"Exact caller number, e.g. +525512345678.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"Exact called number, e.g. +14155551234.","schema":{"type":"string"}},{"name":"channel_id","in":"query","required":false,"description":"Only calls on this channel.","schema":{"type":"string","format":"uuid"}},{"name":"campaign_id","in":"query","required":false,"description":"Only this campaign's calls (needs `calls:read` only).","schema":{"type":"string","format":"uuid"}},{"name":"enrollment_id","in":"query","required":false,"description":"Only the calls to one campaign contact (an enrollment id).","schema":{"type":"string","format":"uuid"}},{"name":"started_after","in":"query","required":false,"description":"Only calls that started at or after this UTC time, e.g. 2026-10-01T00:00:00Z (a `Z` timestamp; offsets are refused).","schema":{"type":"string","format":"date-time"}},{"name":"started_before","in":"query","required":false,"description":"Only calls that started at or before this UTC time (a `Z` timestamp).","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of calls.","content":{"application/json":{"example":{"success":true,"data":[{"id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","direction":"outbound","status":"completed","from_number":"+525512345678","to_number":"+14155551234","started_at":"2026-10-02T02:14:49.108017+00:00","ended_at":"2026-10-02T02:16:49.108017+00:00","duration_seconds":120,"agent_name":"Support Agent","channel_name":"Support Agent - Voice","call_outcome":"completed","failure_reason":null,"failure_detail":null,"is_test":false,"campaign_enrollment_id":null}],"pagination":{"total":1,"limit":10,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/calls/{id}":{"get":{"operationId":"getCall","summary":"Get a call","description":"Returns one call with its transcript, summary, outcome, quality metrics and credits charged.\n\nRequires the `calls:read` scope.\n\n`credits_charged` is a whole number of credits: the charge with its fraction dropped, never rounded up. `credits_charged_exact` is the exact charge, a number with up to 2 decimals (a rate can have decimals; a call is charged per started minute).\n\n`failure_reason` says why an outbound call reached nobody, and `failure_detail` says it in a sentence (English), e.g. `declined` with \"The person or their phone company rejected the call.\". Both are null for a call that connected, an inbound call and a call still going. `failure_reason` is one of: `invalid_number`, `not_in_service`, `unreachable`, `busy`, `declined`, `blocked_spam`, `no_answer`, `voicemail`, `hung_up_right_away`, `answered_no_speech`, `ai_not_connected`, `caller_id_not_allowed`, `destination_not_allowed`, `needs_country_code`, `network_busy`, `line_in_use`, `no_credits`, `do_not_contact`, `not_interested`, `interested`, `callback`, `canceled`, `system_error`, `failed`. It reads what the carrier said (the dial's refusal, the hangup's SIP code), so it can be more precise than `call_outcome`: a declined call keeps `call_outcome: busy`. `failed` means nothing more specific is known; its `failure_detail` then names the code the carrier gave, when it gave one.\n\n`recording_url` tells where the recording is stored (`supabase://…` in NuTalk's private storage, or a carrier URL for older calls). It is not a public link: download the audio with GET /v1/calls/{id}/recording.\n\n`transcript` and `transcript_summary` fill in a few minutes after the call ends, when post-call processing finishes.\n\n`metadata` carries only where the call came from (`source`) and, for a call from your website, the page and the visitor's details. NuTalk's own routing data is not part of it, and neither are the `notes` sent with POST /v1/calls: those are kept as a note on the call for your team, and the API does not return them.","tags":["Calls"],"x-required-scope":"calls:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#getCall"},"parameters":[{"name":"id","in":"path","required":true,"description":"The call id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The call.","content":{"application/json":{"example":{"success":true,"data":{"id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","direction":"outbound","status":"completed","from_number":"+525512345678","to_number":"+14155551234","started_at":"2026-10-02T02:14:49.108017+00:00","ended_at":"2026-10-02T02:16:49.108017+00:00","duration_seconds":120,"agent_name":"Support Agent","channel_name":"Support Agent - Voice","call_outcome":"completed","failure_reason":null,"failure_detail":null,"is_test":false,"campaign_enrollment_id":null,"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","transcript":"Agent: Hi, this is Acme support.\nCaller: Hi, I'd like a demo.","transcript_summary":"The caller asked for a demo; a meeting was offered for Thursday.","recording_url":"supabase://voice-recordings/d3b85d84-ef8f-4c4a-8d18-3fa23e7fd48a/1ba11348-e440-44de-a5cd-5fe62f4d9df6.mp3","metadata":{},"quality_metrics":{"mos":null,"jitter_ms":null,"packet_loss_pct":null},"answered_by":"human","twilio_status":null,"credits_charged":16,"credits_charged_exact":16.5}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No call with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/call-jobs/{id}":{"get":{"operationId":"getCallJob","summary":"Get a queued call's job","description":"Returns the queue job that POST /v1/calls answered with (`queue_job_id`) when it could not place the call within the request. Read it again at its `next_attempt_at` while it waits (about every 30 s when it has none): once `status` is `completed`, `call_id` is the placed call (read it with GET /v1/calls/{id}); `dead` means the queue gave up, with the reason in `error`. Every read counts against the workspace rate limit, and a job can wait for hours while lines are busy.\n\nRequires the `calls:read` scope.\n\n`status` is `queued` (waiting for a free line, or for the next attempt at `next_attempt_at`), `waiting_for_line` (an AiBox line is busy; tried again at `next_attempt_at`), `leased` or `running` (being placed), `completed`, `dead` (no attempts left) or `cancelled`. `next_attempt_at` is null whenever no attempt time is ahead, for instance while the job waits for a line to free up.\n\nOnly outbound-call jobs of your workspace are found; any other id answers 404.\n\nA `dead` job whose `error` is `do_not_contact` was refused at the dial: by then the number was on the do-not-call list (the person opted out while the call waited, or the number as the line dials it is on the list). Nobody was called; do not ask for that call again.","tags":["Calls"],"x-required-scope":"calls:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#getCallJob"},"parameters":[{"name":"id","in":"path","required":true,"description":"The call job id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The job.","content":{"application/json":{"example":{"success":true,"data":{"id":"9718d2fe-ecf2-4630-82c6-599dfcc0f9fd","status":"completed","call_id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","to":"+14155551234","attempts":1,"max_attempts":3,"next_attempt_at":null,"error":null,"created_at":"2026-10-02T02:14:30.000000+00:00","completed_at":"2026-10-02T02:14:49.108017+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No call job with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/calls/{id}/recording":{"get":{"operationId":"getCallRecording","summary":"Download a call's recording","description":"Returns the call's audio, as the dashboard plays it: a 302 redirect to a signed link that expires in 5 minutes (recordings in NuTalk's storage), or the carrier's audio streamed through (`audio/mpeg` or `audio/wav`, with `Range` support). Follow redirects (`curl -L -o call.mp3`) and fetch it again for a fresh link instead of storing it. Every request it serves is recorded in the workspace's audit log with the key's name and prefix (a resumed range is a request too).\n\nRequires the `calls:read` scope.\n\nRecordings carry what the caller said: treat them as customer data, and keep downloads to what your integration needs.","tags":["Calls"],"x-required-scope":"calls:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#getCallRecording"},"parameters":[{"name":"id","in":"path","required":true,"description":"The call id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The audio, streamed from the carrier.","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}},"audio/wav":{"schema":{"type":"string","format":"binary"}}}},"206":{"description":"Part of the audio, for a `Range` request.","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}},"audio/wav":{"schema":{"type":"string","format":"binary"}}}},"302":{"description":"`Location` is a signed link to the audio, valid for 5 minutes.","headers":{"Location":{"description":"The signed link.","schema":{"type":"string","format":"uri"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No call with this id in your workspace (ids of other workspaces answer the same). `not_found`: The call has no recording (not recorded, or not stored yet).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"description":"`internal_error`: The carrier did not return the recording.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/calls/{id}/terminate":{"post":{"operationId":"terminateCall","summary":"Hang up a live call","description":"Ends a call that is still `queued`, `ringing` or `in_progress`. A call on a NuTalk phone number (Telnyx) hangs up at once and is marked `canceled` (`call_outcome: terminated_via_api`). A website call hangs up from the visitor's page within a few seconds: the answer keeps its current `status`, and the call then closes with its real length.\n\nRequires the `calls:write` scope.","tags":["Calls"],"x-required-scope":"calls:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#terminateCall"},"parameters":[{"name":"id","in":"path","required":true,"description":"The call id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The call was ended.","content":{"application/json":{"example":{"success":true,"data":{"id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","status":"canceled","terminated_at":"2026-10-02T02:24:56.096Z","provider":"telnyx"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No call with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The call is not live (it already ended).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"`validation_error`: The call's carrier cannot be hung up from the API (Twilio or AiBox lines).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`internal_error`: The carrier refused the hang-up.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/calls/{id}/notes":{"get":{"operationId":"listCallNotes","summary":"List a call's notes","description":"The notes the team added to the call, oldest first, as the call's page shows them, each with its author's name (never their email).\n\nRequires the `calls:read` scope.\n\n`author` is null for a note NuTalk wrote itself (a test call, an automation).","tags":["Calls"],"x-required-scope":"calls:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#listCallNotes"},"parameters":[{"name":"id","in":"path","required":true,"description":"The call id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The call's notes.","content":{"application/json":{"example":{"success":true,"data":[{"id":"2b4d6f8a-0c1e-4a3b-9d5f-7e9a1c3b5d70","call_id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","content":"Asked for a callback on Friday.","type":"note","author":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"created_at":"2026-10-02T02:20:00.000+00:00","updated_at":"2026-10-02T02:20:00.000+00:00"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No call with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createCallNote","summary":"Add a note to a call","description":"Adds a note to the call, as the note box on the call's page does. The member who created the key writes it.\n\nRequires the `calls:write` scope.","tags":["Calls"],"x-required-scope":"calls:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#createCallNote"},"parameters":[{"name":"id","in":"path","required":true,"description":"The call id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"description":"The note's text.","type":"string","minLength":1,"maxLength":5000}},"required":["content"]},"example":{"content":"Asked for a callback on Friday."}}}},"responses":{"201":{"description":"The note, as saved.","content":{"application/json":{"example":{"success":true,"data":{"id":"2b4d6f8a-0c1e-4a3b-9d5f-7e9a1c3b5d70","call_id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","content":"Asked for a callback on Friday.","type":"note","author":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"created_at":"2026-10-02T02:20:00.000+00:00","updated_at":"2026-10-02T02:20:00.000+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key doesn't add notes to calls (owners, admins, builders, operators and clinic managers do), has left the workspace, or doesn't see every call (calls off, assigned only, some numbers or agents): a key can't apply that limit call by call, so it writes notes on no call.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No call with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/calls/{id}/notes/{note_id}":{"get":{"operationId":"getCallNote","summary":"Get a call note","description":"Gets one of the call's notes.\n\nRequires the `calls:read` scope.","tags":["Calls"],"x-required-scope":"calls:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#getCallNote"},"parameters":[{"name":"id","in":"path","required":true,"description":"The call id.","schema":{"type":"string","format":"uuid"}},{"name":"note_id","in":"path","required":true,"description":"The note id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The note.","content":{"application/json":{"example":{"success":true,"data":{"id":"2b4d6f8a-0c1e-4a3b-9d5f-7e9a1c3b5d70","call_id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","content":"Asked for a callback on Friday.","type":"note","author":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"created_at":"2026-10-02T02:20:00.000+00:00","updated_at":"2026-10-02T02:20:00.000+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No call or note with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateCallNote","summary":"Change a call note","description":"Changes a note's text. Only a note the key's creator wrote, as on the call's page.\n\nRequires the `calls:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA key whose changes wait for approval holds the change until an owner or admin approves it.","tags":["Calls"],"x-required-scope":"calls:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#updateCallNote"},"parameters":[{"name":"id","in":"path","required":true,"description":"The call id.","schema":{"type":"string","format":"uuid"}},{"name":"note_id","in":"path","required":true,"description":"The note id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"description":"The note's text.","type":"string","minLength":1,"maxLength":5000}},"required":["content"]},"example":{"content":"Asked for a callback on Monday instead."}}}},"responses":{"200":{"description":"The note, as saved.","content":{"application/json":{"example":{"success":true,"data":{"id":"2b4d6f8a-0c1e-4a3b-9d5f-7e9a1c3b5d70","call_id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","content":"Asked for a callback on Monday instead.","type":"note","author":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"created_at":"2026-10-02T02:20:00.000+00:00","updated_at":"2026-10-02T03:05:00.000+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateCallNote"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key doesn't add notes to calls (owners, admins, builders, operators and clinic managers do), has left the workspace, or doesn't see every call (calls off, assigned only, some numbers or agents): a key can't apply that limit call by call, so it writes notes on no call. `forbidden`: `details.reason: not_the_author`: only whoever wrote a note edits or deletes it, and the key writes as the member who created it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No call or note with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteCallNote","summary":"Delete a call note","description":"Deletes a note the key's creator wrote, as on the call's page.\n\nRequires the `calls:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA key whose deletes wait for approval holds the delete until an owner or admin approves it.","tags":["Calls"],"x-required-scope":"calls:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/calls#deleteCallNote"},"parameters":[{"name":"id","in":"path","required":true,"description":"The call id.","schema":{"type":"string","format":"uuid"}},{"name":"note_id","in":"path","required":true,"description":"The note id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"2b4d6f8a-0c1e-4a3b-9d5f-7e9a1c3b5d70","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteCallNote"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key doesn't add notes to calls (owners, admins, builders, operators and clinic managers do), has left the workspace, or doesn't see every call (calls off, assigned only, some numbers or agents): a key can't apply that limit call by call, so it writes notes on no call. `forbidden`: `details.reason: not_the_author`: only whoever wrote a note edits or deletes it, and the key writes as the member who created it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No call or note with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations":{"get":{"operationId":"listConversations","summary":"List conversations","description":"Lists the workspace's messaging conversations, newest activity first, with the channel, customer, CRM contact, assignee, AI handling and reply window of each. Archived conversations are left out unless `archived` asks for them, as in the inbox.\n\nRequires the `conversations:read` scope.\n\nConversations are the inbox's threads on WhatsApp (the official API and the sync bridge), SMS, Instagram, Messenger, Apple Messages and the website chat, from before and after each channel moved to the current messaging engine. Phone calls are in the Calls API (`GET /v1/calls`), with their transcripts.\n\n`status` is `active` or `resolved`. Archiving is separate: `archived` is true for a conversation closed after inactivity (`archive_reason: auto_close_inactivity`), archived by hand (`manual`) or set aside when the WhatsApp account changed (`whatsapp_account_changed`).\n\n`contact` is the CRM contact the inbox's contact panel shows: the contact linked by hand, else the owner of the phone number (`52` and `521` spellings of a Mexican mobile are one number), else the owner of the Instagram, Messenger, Apple Messages or website identity. Read the contact itself with `GET /v1/contacts/{id}` (needs `contacts:read`).\n\n`ai.mode` is what the AI does now, with the channel's own setting applied: `ai_active` (replies), `human_takeover` or `paused` (does not reply; `ai.answers` is false). `ai.hold` is set while a teammate holds the conversation (`reason: operator_hold`) or a paused channel assigned it to the teammate who answered (`paused_channel_autoclaim`).\n\n`awaiting_reply` is true when the customer wrote last and nobody (the AI or a teammate) has answered yet; `unread_count` counts those customer messages. There is no per-person read state. `waiting_for_human_since` is when a customer started waiting on a conversation the AI is not answering.\n\n`reply_window` says whether a free-form reply may be sent now: official WhatsApp, Instagram and Messenger accept one only within 24 hours of the customer's last message (`applies: true`); the WhatsApp sync bridge, SMS, Apple Messages and the website chat have no window.","tags":["Conversations"],"x-required-scope":"conversations:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#listConversations"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"channel","in":"query","required":false,"description":"Only conversations on this kind of channel (`sms`: text messages; `imessage`: Apple Messages; `web_widget`: the website chat).","schema":{"type":"string","enum":["whatsapp","sms","instagram","messenger","imessage","web_widget"]}},{"name":"channel_id","in":"query","required":false,"description":"Only conversations on this channel (see GET /v1/channels).","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Only open (`active`) or closed (`resolved`) conversations.","schema":{"type":"string","enum":["active","resolved"]}},{"name":"archived","in":"query","required":false,"description":"`exclude` leaves archived conversations out, `include` lists them too, `only` lists nothing else.","schema":{"type":"string","enum":["exclude","include","only"],"default":"exclude"}},{"name":"assignee_id","in":"query","required":false,"description":"Only conversations assigned to this teammate (a user id), or `none` for unassigned ones.","schema":{"type":"string"}},{"name":"contact_id","in":"query","required":false,"description":"Only the conversations whose `contact` is this CRM contact.","schema":{"type":"string","format":"uuid"}},{"name":"search","in":"query","required":false,"description":"A phone number (matched on its last 10 digits) or part of the name the customer has on the channel. To find a conversation by its CRM contact, use `contact_id`.","schema":{"type":"string","maxLength":100}},{"name":"awaiting_reply","in":"query","required":false,"description":"`true`: the customer wrote last and nobody has answered. `false`: the opposite.","schema":{"type":"boolean"}},{"name":"last_message_after","in":"query","required":false,"description":"Only conversations whose last message is at or after this time, e.g. 2026-10-01T00:00:00Z.","schema":{"type":"string","format":"date-time"}},{"name":"last_message_before","in":"query","required":false,"description":"Only conversations whose last message is at or before this time. A filter, not a cursor: to page, use `before`.","schema":{"type":"string","format":"date-time"}},{"name":"before","in":"query","required":false,"description":"The `pagination.next_before` of your previous page: continues the list right after that page's last conversation, with no repeats or gaps even when conversations share a timestamp. Replaces `offset`, and is the way past the first 1,000 conversations; with it, `total` counts the conversations from there on.","schema":{"type":"string"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of conversations.","content":{"application/json":{"example":{"success":true,"data":[{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support WhatsApp","type":"whatsapp"},"customer":{"name":"Ana","handle":"5215512345678","handle_type":"phone"},"contact":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","name":"Ana López"},"status":"active","archived":false,"archived_at":null,"archive_reason":null,"assignee":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"},"ai":{"mode":"ai_active","answers":true,"channel_mode":"ai_active","hold":null},"unread_count":2,"awaiting_reply":true,"waiting_for_human_since":null,"last_message_at":"2026-10-02T12:15:08.634668+00:00","last_message_preview":"¿Siguen ahí?","last_customer_message_at":"2026-10-02T12:15:08.634668+00:00","reply_window":{"applies":true,"open":true,"expires_at":"2026-10-03T12:15:08.634Z"},"created_at":"2026-09-30T12:45:08.634668+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false,"next_before":null}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: `offset` + `limit` is over 1,000: continue with `before` (the previous page's `pagination.next_before`). `validation_error`: `before` is not a `next_before` from this API, or is sent with `offset`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}":{"get":{"operationId":"getConversation","summary":"Get a conversation","description":"Returns one conversation with everything the list returns, plus the AI's summary of the last finished exchange, the number of messages and the conversation's evaluation (overall score, goals met, competency levels).\n\nRequires the `conversations:read` scope.\n\n`summary` is the AI memory's summary of the last finished exchange (an exchange ends when the conversation goes quiet for a while). It is written for conversations with a phone number, so it is null for Instagram, Messenger and website visitors, and until a first exchange has ended.\n\n`evaluation.status` is `evaluated`, or the state of a pending evaluation (`pending`, `processing`, `failed` with `error`), or `none`. `overall_score` is 0–100; each competency `score` is the rubric level reached, 1 (lowest) to 5. Define goals and competencies with the Goals and Competencies APIs.\n\nTest conversations, simulations and calls are not conversations here: their ids answer 404, like the ids of other workspaces.","tags":["Conversations"],"x-required-scope":"conversations:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#getConversation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The conversation.","content":{"application/json":{"example":{"success":true,"data":{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support WhatsApp","type":"whatsapp"},"customer":{"name":"Ana","handle":"5215512345678","handle_type":"phone"},"contact":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","name":"Ana López"},"status":"active","archived":false,"archived_at":null,"archive_reason":null,"assignee":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"},"ai":{"mode":"ai_active","answers":true,"channel_mode":"ai_active","hold":null},"unread_count":2,"awaiting_reply":true,"waiting_for_human_since":null,"last_message_at":"2026-10-02T12:15:08.634668+00:00","last_message_preview":"¿Siguen ahí?","last_customer_message_at":"2026-10-02T12:15:08.634668+00:00","reply_window":{"applies":true,"open":true,"expires_at":"2026-10-03T12:15:08.634Z"},"created_at":"2026-09-30T12:45:08.634668+00:00","summary":"Ana asked to move her Tuesday appointment to Thursday.","message_count":8,"evaluation":{"status":"evaluated","evaluated_at":"2026-10-02T13:02:41.118207+00:00","overall_score":82,"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","title":"Book appointment","met":true,"justification":"The customer confirmed Thursday at 10:00."}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","name":"Empathy","level":"Proficient","score":4,"justification":"Acknowledged the change of plans before offering slots."}],"error":null}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateConversation","summary":"Assign a conversation","description":"Assigns the conversation to a teammate, or unassigns it with `null`. The teammate is notified in their inbox, as for an assignment made there.\n\nRequires the `conversations:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nAsking for the state the conversation is already in changes nothing and records nothing; the response is the conversation either way.\n\nEvery change is written to the workspace's audit log with the API key as its author.\n\nThe notification reads \"assigned by a teammate\": an API key has no person behind it.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#updateConversation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assignee_id":{"description":"A teammate's user id, or null to unassign. The teammate must be a member with the owner, admin, operator, clinic manager, clinic messages or receptionist role. Teammates' ids appear as `assignee.id` and `ai.hold.by.id` on conversations.","type":["string","null"],"format":"uuid"}},"required":["assignee_id"]},"example":{"assignee_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14"}}}},"responses":{"200":{"description":"The conversation, assigned.","content":{"application/json":{"example":{"success":true,"data":{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support WhatsApp","type":"whatsapp"},"customer":{"name":"Ana","handle":"5215512345678","handle_type":"phone"},"contact":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","name":"Ana López"},"status":"active","archived":false,"archived_at":null,"archive_reason":null,"assignee":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"},"ai":{"mode":"ai_active","answers":true,"channel_mode":"ai_active","hold":null},"unread_count":2,"awaiting_reply":true,"waiting_for_human_since":null,"last_message_at":"2026-10-02T12:15:08.634668+00:00","last_message_preview":"¿Siguen ahí?","last_customer_message_at":"2026-10-02T12:15:08.634668+00:00","reply_window":{"applies":true,"open":true,"expires_at":"2026-10-03T12:15:08.634Z"},"created_at":"2026-09-30T12:45:08.634668+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateConversation"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, `assignee_id` is missing or not a UUID, it names someone who is not an assignable member, or another field is sent (status and archiving are the actions below).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `details.reason: pre_cutover_conversation`: a website or Apple Messages conversation from before its channel moved to the current inbox; it can only be archived or unarchived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}/messages":{"get":{"operationId":"listConversationMessages","summary":"List a conversation's messages","description":"Lists a conversation's messages, newest first by default, with who sent each one, its text, attachments (with voice-note transcriptions and image descriptions), shared location, template and delivery status.\n\nRequires the `conversations:read` scope.\n\n`sender.type` says who wrote: `customer`, `ai` (the agent's reply), `teammate` (sent from the inbox; `user_id` and `name` say who), `automation` (reminders and follow-ups), `business_app` (sent from the business's own phone, app or Mac outside NuTalk) or `system`.\n\n`created_at` is when the message was sent; `stored_at` is when NuTalk stored it. They differ for a message stored after the fact (recovered after an outage, or synced from the business's phone), which is why `after` follows storage order.\n\nWith `after`, `pagination` adds `next_after`, the point to continue from: the same value when nothing new was stored. `total` and `has_more` count the messages after the point you passed. A message is returned a few seconds after it is stored, so one still being written is never skipped. A message NuTalk stores again (a recovery after an outage) comes back once more: deduplicate by `id`.\n\n`text` is the message text, or a media message's caption. Attachment `url`s are links to the stored copies the inbox shows: a file NuTalk stores is a signed link that works for one hour (one day in a `message.received` webhook), so download it when you read it and read the message again for a fresh link; files of Apple Messages conversations from before the current engine are described without a link.\n\nInternal rows are not listed: the agent's tool output, placeholders for AI replies that were never produced, and reactions and channel notices that only show in the inbox. `status` is the delivery status as stored: the customer's messages are `delivered` (`received` in conversations from before the current engine); replies go `pending`, `sent`, `delivered`, `read`, or `failed` with the reason in `error`.","tags":["Conversations"],"x-required-scope":"conversations:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#listConversationMessages"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":50,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"order","in":"query","required":false,"description":"`desc`: newest first. `asc`: oldest first, to read a conversation from the start.","schema":{"type":"string","enum":["desc","asc"],"default":"desc"}},{"name":"after","in":"query","required":false,"description":"`start`, or the `pagination.next_after` of your previous read: returns the messages stored after that point, oldest stored first, and the next `next_after`. Follow a conversation with it; leave out `offset`, `order`, `created_after` and `created_before`.","schema":{"type":"string"}},{"name":"created_after","in":"query","required":false,"description":"Only messages sent after this time (not at it). A message recovered after an outage keeps its earlier send time, so to follow a conversation use `after`.","schema":{"type":"string","format":"date-time"}},{"name":"created_before","in":"query","required":false,"description":"Only messages sent before this time.","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of messages.","content":{"application/json":{"example":{"success":true,"data":[{"id":"0e6a4c2b-7f1d-4b3e-8a5c-9d2f4b6e8a10","direction":"inbound","sender":{"type":"customer","user_id":null,"name":null},"text":null,"attachments":[{"type":"voice","url":"https://api.nutalk.ai/storage/v1/object/sign/whatsapp-media/…/voice.ogg?token=…","mime_type":"audio/ogg","file_name":null,"duration_seconds":7,"transcription":"I'd like to move my appointment to Thursday.","description":null}],"location":null,"template":null,"status":"delivered","error":null,"created_at":"2026-10-02T12:12:08.634668+00:00","stored_at":"2026-10-02T12:12:08.902115+00:00","sent_at":null,"delivered_at":null,"read_at":null}],"pagination":{"total":1,"limit":50,"offset":0,"has_more":false,"next_after":"WzEsIjIwMjYtMTAtMDJUMTI6MTI6MDguOTAyMTE1KzAwOjAwIiwiMjAyNi0xMC0wMlQxMjoxMjowOC42MzQ2NjgrMDA6MDAiLCIwZTZhNGMyYi03ZjFkLTRiM2UtOGE1Yy05ZDJmNGI2ZThhMTAiXQ"}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: `after` is neither `start` nor a `next_after` from this API, or is sent with `offset`, `order` or a `created_` filter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"sendConversationMessage","summary":"Send a message","description":"Sends a text, or an approved WhatsApp template, to the conversation's customer through the conversation's own channel, as a reply from the inbox does. Requires an `Idempotency-Key`: a retry with the same key never sends twice.\n\nRequires the `conversations:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe message goes out as a reply from the inbox does: through the conversation's channel, billed in message credits, and it resets the conversation's `unread_count` and \"customer waiting\" state.\n\nIt goes out under the name \"API · <key name>\" on behalf of the teammate who created the key, who must still be a member of the workspace (`key_owner_not_member` otherwise: create a new key). The audit log records the key.\n\nA send does not change the AI: it keeps answering the customer's next message unless you pause it first. A resolved conversation stays resolved until the customer answers; an archived one is refused.\n\nMessages reach real people: confirm the text and the conversation with the user before an AI agent sends.\n\nA key whose send was interrupted, or went out without its answer being recorded, stays taken for 24 hours (409 `IDEMPOTENCY_CONFLICT`): check the conversation's messages before sending again with a new key.\n\nFor a text, the `Idempotency-Key` also names the message itself, down to the channel: the same text sent again with the same key is never delivered twice, even after the key's 24-hour replay window. The answer is then the message already sent, or 202 `outcome: \"unknown\"` while its first attempt is unconfirmed. A template send relies on the key's replay alone.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#sendConversationMessage"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"description":"The message. Send either `text` or `template`.","type":"string","maxLength":4096},"template":{"description":"An approved WhatsApp template, for official WhatsApp conversations: the only message they accept after the 24-hour window.","type":"object","properties":{"name":{"description":"The template's name.","type":"string"},"language":{"description":"The exact approved translation, e.g. `es_MX` or `en_US`; never guessed.","type":"string"},"variables":{"description":"Placeholder values, positional (`{\"1\": \"Ana\"}`) or named (`{\"customer_name\": \"Ana\"}`).","type":"object","additionalProperties":true}},"required":["name","language"]}}},"example":{"text":"Your order shipped today. It arrives Thursday."}}}},"responses":{"201":{"description":"Sent: the message as stored.","content":{"application/json":{"example":{"success":true,"data":{"id":"0e6a4c2b-7f1d-4b3e-8a5c-9d2f4b6e8a10","direction":"outbound","sender":{"type":"teammate","user_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"API · Order updates"},"text":"Your order shipped today. It arrives Thursday.","attachments":[],"location":null,"template":null,"status":"sent","error":null,"created_at":"2026-10-02T14:05:11.302114+00:00","stored_at":"2026-10-02T14:05:11.302114+00:00","sent_at":"2026-10-02T14:05:11.29+00:00","delivered_at":null,"read_at":null}}}}},"202":{"description":"The channel did not confirm the message in time: it may have been delivered. Check the conversation's messages before sending it again; retrying with the same Idempotency-Key returns this answer again.","content":{"application/json":{"example":{"success":true,"data":null,"outcome":"unknown","message":"The channel did not confirm the message in time; it may have been delivered. Check the conversation's messages before sending it again."}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The `Idempotency-Key` header is missing; both or neither of `text` and `template` are sent; a template variable is empty; or (`details.reason: template_not_found`) the template has no approved translation in that language (`details.available_languages`). `validation_error`: `details.reason: provider_rejected`: WhatsApp refused the template. `message` is its explanation and `details.provider_error.code` its error code (e.g. 131031 account locked, 132001 template unknown to WhatsApp). `validation_error`: `details.reason: marketing_to_us_number`: a marketing template to a US number, which WhatsApp doesn't deliver. Nothing is sent or charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"`validation_error`: `details.reason: insufficient_credits`: the workspace has no credits left for a template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `details.reason: reply_window_closed`: official WhatsApp, Instagram or Messenger, 24 hours after the customer's last message (`details.expires_at`); `details.template_required` says whether a template can still go. `validation_error`: `details.reason: template_not_sendable`: WhatsApp holds the template now (`details.template_status`, e.g. PAUSED, DISABLED or ARCHIVED). Nothing is sent or charged. `validation_error`: `details.reason: whatsapp_payment_hold`: Meta can't charge the WhatsApp account (it refused a template for payment, 131042), so NuTalk holds its templates until a payment method works in Meta (`details.held_since`). Nothing is sent or charged. Replies inside the 24 hours still go. `validation_error`: `details.reason`: `conversation_archived` (unarchive it first), `teammate_takeover`, `pre_cutover_conversation`, `templates_unsupported` (not an official WhatsApp conversation), `channel_unavailable`, `unsupported_conversation`, or `key_owner_not_member` (see the notes). `validation_error`: The `Idempotency-Key` already names another message in this conversation: use a new key for a new message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`IDEMPOTENCY_UNAVAILABLE`: The `Idempotency-Key` could not be recorded, so nothing was sent: retry with the same key after `Retry-After` seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conversations/{id}/resolve":{"post":{"operationId":"resolveConversation","summary":"Resolve a conversation","description":"Closes the conversation, as Resolve does in the inbox. A teammate's takeover ends with it; a conversation whose AI was switched off stays off. The customer's next message reopens it.\n\nRequires the `conversations:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nAsking for the state the conversation is already in changes nothing and records nothing; the response is the conversation either way.\n\nEvery change is written to the workspace's audit log with the API key as its author.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#resolveConversation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The conversation, resolved.","content":{"application/json":{"example":{"success":true,"data":{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support WhatsApp","type":"whatsapp"},"customer":{"name":"Ana","handle":"5215512345678","handle_type":"phone"},"contact":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","name":"Ana López"},"status":"resolved","archived":false,"archived_at":null,"archive_reason":null,"assignee":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"},"ai":{"mode":"ai_active","answers":true,"channel_mode":"ai_active","hold":null},"unread_count":2,"awaiting_reply":true,"waiting_for_human_since":null,"last_message_at":"2026-10-02T12:15:08.634668+00:00","last_message_preview":"¿Siguen ahí?","last_customer_message_at":"2026-10-02T12:15:08.634668+00:00","reply_window":{"applies":true,"open":true,"expires_at":"2026-10-03T12:15:08.634Z"},"created_at":"2026-09-30T12:45:08.634668+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"resolveConversation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `details.reason: pre_cutover_conversation`: a website or Apple Messages conversation from before its channel moved to the current inbox; it can only be archived or unarchived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}/reopen":{"post":{"operationId":"reopenConversation","summary":"Reopen a conversation","description":"Reopens a resolved conversation (`status: active`).\n\nRequires the `conversations:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nAsking for the state the conversation is already in changes nothing and records nothing; the response is the conversation either way.\n\nEvery change is written to the workspace's audit log with the API key as its author.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#reopenConversation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The conversation, open.","content":{"application/json":{"example":{"success":true,"data":{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support WhatsApp","type":"whatsapp"},"customer":{"name":"Ana","handle":"5215512345678","handle_type":"phone"},"contact":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","name":"Ana López"},"status":"active","archived":false,"archived_at":null,"archive_reason":null,"assignee":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"},"ai":{"mode":"ai_active","answers":true,"channel_mode":"ai_active","hold":null},"unread_count":2,"awaiting_reply":true,"waiting_for_human_since":null,"last_message_at":"2026-10-02T12:15:08.634668+00:00","last_message_preview":"¿Siguen ahí?","last_customer_message_at":"2026-10-02T12:15:08.634668+00:00","reply_window":{"applies":true,"open":true,"expires_at":"2026-10-03T12:15:08.634Z"},"created_at":"2026-09-30T12:45:08.634668+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"reopenConversation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `details.reason: pre_cutover_conversation`: a website or Apple Messages conversation from before its channel moved to the current inbox; it can only be archived or unarchived. `validation_error`: `details.reason: conversation_archived`: the conversation is archived; unarchive it, which also reopens it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}/archive":{"post":{"operationId":"archiveConversation","summary":"Archive a conversation","description":"Archives the conversation as archiving it by hand in the inbox does: it is resolved, moves to the archive (`archive_reason: manual`) and stays there when the customer writes again.\n\nRequires the `conversations:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nAsking for the state the conversation is already in changes nothing and records nothing; the response is the conversation either way.\n\nEvery change is written to the workspace's audit log with the API key as its author.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#archiveConversation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The conversation, archived.","content":{"application/json":{"example":{"success":true,"data":{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support WhatsApp","type":"whatsapp"},"customer":{"name":"Ana","handle":"5215512345678","handle_type":"phone"},"contact":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","name":"Ana López"},"status":"resolved","archived":true,"archived_at":"2026-10-02T13:20:00.000+00:00","archive_reason":"manual","assignee":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"},"ai":{"mode":"ai_active","answers":true,"channel_mode":"ai_active","hold":null},"unread_count":2,"awaiting_reply":true,"waiting_for_human_since":null,"last_message_at":"2026-10-02T12:15:08.634668+00:00","last_message_preview":"¿Siguen ahí?","last_customer_message_at":"2026-10-02T12:15:08.634668+00:00","reply_window":{"applies":true,"open":true,"expires_at":"2026-10-03T12:15:08.634Z"},"created_at":"2026-09-30T12:45:08.634668+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"archiveConversation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}/unarchive":{"post":{"operationId":"unarchiveConversation","summary":"Unarchive a conversation","description":"Brings an archived conversation back to the inbox, open (`status: active`).\n\nRequires the `conversations:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nAsking for the state the conversation is already in changes nothing and records nothing; the response is the conversation either way.\n\nEvery change is written to the workspace's audit log with the API key as its author.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#unarchiveConversation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The conversation, back in the inbox.","content":{"application/json":{"example":{"success":true,"data":{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support WhatsApp","type":"whatsapp"},"customer":{"name":"Ana","handle":"5215512345678","handle_type":"phone"},"contact":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","name":"Ana López"},"status":"active","archived":false,"archived_at":null,"archive_reason":null,"assignee":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"},"ai":{"mode":"ai_active","answers":true,"channel_mode":"ai_active","hold":null},"unread_count":2,"awaiting_reply":true,"waiting_for_human_since":null,"last_message_at":"2026-10-02T12:15:08.634668+00:00","last_message_preview":"¿Siguen ahí?","last_customer_message_at":"2026-10-02T12:15:08.634668+00:00","reply_window":{"applies":true,"open":true,"expires_at":"2026-10-03T12:15:08.634Z"},"created_at":"2026-09-30T12:45:08.634668+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"unarchiveConversation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `details.reason: whatsapp_account_changed`: the conversation belongs to a WhatsApp account that is no longer linked to its channel.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}/pause-ai":{"post":{"operationId":"pauseConversationAi","summary":"Pause the AI on a conversation","description":"Switches the AI off on the conversation so a person answers: the inbox's AI-off switch. The switch is shared by the team: any teammate can turn the AI back on in the inbox, or with resume-ai.\n\nRequires the `conversations:write` scope.\n\nAsking for the state the conversation is already in changes nothing and records nothing; the response is the conversation either way.\n\nEvery change is written to the workspace's audit log with the API key as its author.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#pauseConversationAi"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The conversation, with the AI off.","content":{"application/json":{"example":{"success":true,"data":{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support WhatsApp","type":"whatsapp"},"customer":{"name":"Ana","handle":"5215512345678","handle_type":"phone"},"contact":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","name":"Ana López"},"status":"active","archived":false,"archived_at":null,"archive_reason":null,"assignee":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"},"ai":{"mode":"paused","answers":false,"channel_mode":"ai_active","hold":{"by":null,"since":"2026-10-02T13:30:00.000+00:00","reason":"operator_hold"}},"unread_count":2,"awaiting_reply":true,"waiting_for_human_since":null,"last_message_at":"2026-10-02T12:15:08.634668+00:00","last_message_preview":"¿Siguen ahí?","last_customer_message_at":"2026-10-02T12:15:08.634668+00:00","reply_window":{"applies":true,"open":true,"expires_at":"2026-10-03T12:15:08.634Z"},"created_at":"2026-09-30T12:45:08.634668+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `details.reason: pre_cutover_conversation`: a conversation from before its channel moved to the current inbox; it receives no new messages, so it has no AI switch. `validation_error`: `details.reason: teammate_takeover`: a teammate took the conversation over themselves. The AI does not answer while they hold it, and only they (or a workspace admin, in the inbox) can hand it back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}/resume-ai":{"post":{"operationId":"resumeConversationAi","summary":"Resume the AI on a conversation","description":"Switches the AI back on for this conversation, as \"Let AI answer\" in the inbox does: the agent answers from the customer's next message and never replies to an earlier one, so `reply_scheduled` is always `false` (kept for compatibility). The conversation keeps the AI when the channel's mode changes, until the channel is paused.\n\nRequires the `conversations:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nAsking for the state the conversation is already in changes nothing and records nothing; the response is the conversation either way.\n\nEvery change is written to the workspace's audit log with the API key as its author.\n\nOn a resolved or archived conversation, or on a channel whose AI is paused, the AI is recorded as on and nothing is sent: it answers once the conversation or the channel is active again.\n\nWithout a deployed agent, resuming only releases the AI-off switch; nobody answers until an agent is deployed on the channel.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#resumeConversationAi"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The conversation, with the AI on.","content":{"application/json":{"example":{"success":true,"data":{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support WhatsApp","type":"whatsapp"},"customer":{"name":"Ana","handle":"5215512345678","handle_type":"phone"},"contact":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","name":"Ana López"},"status":"active","archived":false,"archived_at":null,"archive_reason":null,"assignee":{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez"},"agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"},"ai":{"mode":"ai_active","answers":true,"channel_mode":"ai_active","hold":null},"unread_count":2,"awaiting_reply":true,"waiting_for_human_since":null,"last_message_at":"2026-10-02T12:15:08.634668+00:00","last_message_preview":"¿Siguen ahí?","last_customer_message_at":"2026-10-02T12:15:08.634668+00:00","reply_window":{"applies":true,"open":true,"expires_at":"2026-10-03T12:15:08.634Z"},"created_at":"2026-09-30T12:45:08.634668+00:00"},"reply_scheduled":false}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"resumeConversationAi"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `details.reason: pre_cutover_conversation`: a conversation from before its channel moved to the current inbox; it receives no new messages, so it has no AI switch. `validation_error`: `details.reason: teammate_takeover`: a teammate took the conversation over themselves. The AI does not answer while they hold it, and only they (or a workspace admin, in the inbox) can hand it back. `validation_error`: `details.reason: no_agent`: no agent is deployed on the conversation's channel, and the AI was not switched off.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}/reply-now":{"post":{"operationId":"replyNowConversation","summary":"Ask the AI to reply now","description":"The AI answers the customer's last message now, as \"Reply now\" in the inbox: for a message that came in before the AI took the conversation, which it does not answer by itself. Name that message in `inbound_message_id` (its id from GET /v1/conversations/{id}/messages): if the customer wrote again since, nothing is asked, so the AI never answers a message you did not mean. The agent writes, bills and sends the reply as any of its own.\n\nRequires the `conversations:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nAsking twice is safe: the second request answers `answering` and queues nothing.\n\nEvery change is written to the workspace's audit log with the API key as its author.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#replyNowConversation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"inbound_message_id":{"description":"The customer's last message: the one the AI answers.","type":"string","format":"uuid"}},"required":["inbound_message_id"]},"example":{"inbound_message_id":"0e6a4c2b-7f1d-4b3e-8a5c-9d2f4b6e8a10"}}}},"responses":{"200":{"description":"Asked (`queued`), or a reply to that message is already on its way (`answering`: nothing more is queued).","content":{"application/json":{"example":{"success":true,"data":{"status":"queued","job_id":"9718d2fe-ecf2-4630-82c6-599dfcc0f9fd","inbound_message_id":"0e6a4c2b-7f1d-4b3e-8a5c-9d2f4b6e8a10"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"replyNowConversation"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: `details.reason: pre_cutover_conversation`: a conversation from before its channel moved to the current inbox; the AI does not answer in it. `conflict`: The AI was not asked (`details.reason`): `customer_wrote_again` (name their last message), `already_answered`, `taken_over` (a teammate or the AI-off switch holds it), `ai_not_answering`, `channel_paused`, `no_agent`, `window_closed`, `started_over`, `needs_review`, `closed` (resolved or archived), `no_customer_message` or `superseded`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}/retry-ai":{"post":{"operationId":"retryConversationAi","summary":"Retry the AI's failed reply","description":"Runs the conversation's failed AI reply again, as Retry in the inbox: the agent answers the customer's message its last turn failed on. A failed turn writes nothing to the customer, so the messages list does not show it; this finds it.\n\nRequires the `conversations:write` scope.\n\nOnly the AI's own replies: a message a teammate sent is sent again from the inbox, and one sent with the API by repeating its request with the same `Idempotency-Key`.\n\nEvery change is written to the workspace's audit log with the API key as its author.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#retryConversationAi"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Queued again (`queued`), or the turn is already running (`already_running`: nothing more is queued).","content":{"application/json":{"example":{"success":true,"data":{"status":"queued","job_id":"9718d2fe-ecf2-4630-82c6-599dfcc0f9fd"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: `details.reason: pre_cutover_conversation`: a conversation from before its channel moved to the current inbox; the AI does not answer in it. `conflict`: Nothing was queued (`details.reason`): `no_failed_turn` (the AI has no failed reply here), `superseded` (the customer wrote again: the AI answers their newest message), `already_answered`, `ai_paused`, `no_agent`, `window_expired`, `needs_review`, `undeliverable_reply`, or another reason a teammate answers from the inbox.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/bulk-ai":{"post":{"operationId":"bulkConversationAi","summary":"Switch the AI on many conversations","description":"Switches the AI off (or back on) on up to 50 conversations at once, as the inbox's bulk bar does: each one as POST /v1/conversations/{id}/pause-ai or resume-ai would, with the same rules. Each conversation gets its own result; one refused doesn't stop the others.\n\nRequires the `conversations:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA conversation that can't be switched has `ok: false` and `error` (`message`, `reason`): `not_found` (not in your workspace), or the reasons pause-ai and resume-ai answer (`pre_cutover_conversation`, `teammate_takeover`, `no_agent`); `reason` is null when it failed on our side.\n\nA key whose changes wait for approval holds `on` (as it holds resume-ai); `off` is never held: stopping the AI is a safety action.\n\nEvery change is written to the workspace's audit log with the API key as its author.","tags":["Conversations"],"x-required-scope":"conversations:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#bulkConversationAi"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversation_ids":{"description":"The conversations, each once.","type":"array","minItems":1,"maxItems":50,"items":{"description":"A conversation id.","type":"string","format":"uuid"}},"ai":{"description":"`off`: a person answers. `on`: the AI answers from each customer's next message.","type":"string","enum":["on","off"]}},"required":["conversation_ids","ai"]},"example":{"conversation_ids":["5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13"],"ai":"off"}}}},"responses":{"200":{"description":"One result per conversation, in the order sent. `switched` counts the ones that are now as asked.","content":{"application/json":{"example":{"success":true,"data":{"ai":"off","switched":1,"results":[{"id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","ok":true,"ai":{"mode":"paused","answers":false,"channel_mode":"ai_active","hold":{"by":null,"since":"2026-10-02T13:30:00.000+00:00","reason":"operator_hold"}}}]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"bulkConversationAi"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversations/{id}/templates":{"get":{"operationId":"listConversationTemplates","summary":"List the templates a chat can send","description":"The WhatsApp templates this chat can send, as the inbox's template picker lists them: suggested ones first, then every approved one, then the ones WhatsApp or NuTalk doesn't send now, each with why, and with its placeholders filled for the chat's contact.\n\nRequires the `conversations:read` scope.\n\n`sendable` is false while WhatsApp holds a template (`reason.kind` `paused`, with `until`; `disabled`; `archived`), for a marketing template to a US number (`us_marketing`), for a kind of template a chat doesn't send (`unsupported`), and for one whose header or link takes a value, or whose header needs a file (a document, or an image or a video without an approved sample) (`inbox_only`): this API fills a template's body only and attaches no file, so the inbox sends those. `suggested` ones fill the contact's next appointment (`appointment`) or are the team's most sent this week (`most_sent`); the rest are `approved`.\n\nSend one with POST /v1/conversations/{id}/messages and `template`: its `name`, its `language` and, as `variables`, each `value` by its `key` (fill the empty ones). The header takes the message's value of the same key. A template whose link takes a value (`part: button`) is sent from the inbox for now.\n\n`payment_hold` is set while Meta can't charge the WhatsApp account: its templates don't go out until a payment method works in Meta.\n\nThe list is NuTalk's copy of the account's templates, kept current by WhatsApp's status updates and the dashboard: one created in WhatsApp Manager shows once the dashboard has read WhatsApp again.","tags":["Conversations"],"x-required-scope":"conversations:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#listConversationTemplates"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversation id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The chat's templates.","content":{"application/json":{"example":{"success":true,"data":{"conversation_id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","account_name":"Support WhatsApp","payment_hold":null,"templates":[{"name":"appointment_reminder","language":"es_MX","category":"UTILITY","title":"Appointment reminder","header":"none","sendable":true,"suggested":true,"reason":{"kind":"appointment","starts_at":"2026-10-28T16:00:00.000Z","timezone":"America/Mexico_City"},"variables":[{"key":"1","part":"body","label":"First name","value":"Ana"},{"key":"2","part":"body","label":"Date","value":""}]}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: `details.reason`: `not_whatsapp_api` (the chat is not on an official WhatsApp number) or `no_account` (its number has no WhatsApp account linked).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/teammates":{"get":{"operationId":"listTeammates","summary":"List teammates","description":"The workspace's members, oldest first, with their role and whether a conversation can be assigned to them (PATCH /v1/conversations/{id} with `assignee_id`). Never an email address or a phone number.\n\nRequires the `conversations:read` scope.\n\n`name` is null for a member who hasn't set one.","tags":["Conversations"],"x-required-scope":"conversations:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversations#listTeammates"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of teammates.","content":{"application/json":{"example":{"success":true,"data":[{"id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","name":"María Pérez","role":"operator","can_be_assigned":true}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contacts":{"get":{"operationId":"listContacts","summary":"List contacts","description":"Lists contacts, newest first, with optional search and tag filters. Each contact carries `fields`: its contact field values by key.\n\nRequires the `contacts:read` scope.","tags":["Contacts"],"x-required-scope":"contacts:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#listContacts"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"search","in":"query","required":false,"description":"Substring of the phone number, first, last or display name, or email (case-insensitive; commas and parentheses are ignored).","schema":{"type":"string"}},{"name":"tag","in":"query","required":false,"description":"Only contacts carrying this tag.","schema":{"type":"string"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of contacts.","content":{"application/json":{"example":{"success":true,"data":[{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+14155551234","first_name":"Jane","last_name":"Smith","display_name":null,"email":"jane@example.com","company":"Tech Startup","notes":null,"tags":["prospect","inbound"],"source":"api","do_not_contact_at":null,"first_contact_at":null,"last_contact_at":"2026-10-02T02:14:49.108017+00:00","created_at":"2026-10-02T02:25:06.947918+00:00","updated_at":"2026-10-02T02:25:06.947918+00:00","fields":{"interes":"Departamento","presupuesto":"2500000"}}],"pagination":{"total":1,"limit":10,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createContact","summary":"Create a contact","description":"Creates a contact. The phone number must be international (a `+` and the country code): the API never guesses a country. Each phone number belongs to one contact per workspace; Mexican mobile numbers with and without the `1` after `+52` count as the same number.\n\nRequires the `contacts:write` scope.","tags":["Contacts"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#createContact"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_number":{"description":"E.164 number with the country code, e.g. +14155551234.","type":"string","maxLength":20},"first_name":{"description":"First name.","type":"string","maxLength":100},"last_name":{"description":"Last name.","type":"string","maxLength":100},"display_name":{"description":"Name to show instead of first + last.","type":"string","maxLength":200},"email":{"description":"Email address.","type":"string","format":"email"},"company":{"description":"Company.","type":"string","maxLength":200},"notes":{"description":"Internal notes.","type":"string","maxLength":2000},"tags":{"description":"Tags. On update the list replaces the current tags.","type":"array","items":{"description":"A tag.","type":"string"}},"fields":{"description":"Contact field values by field key (GET /v1/contact-fields lists the keys), e.g. `{\"interes\": \"Departamento\"}`. A value is a string, number or boolean and must fit the field's type (a number field takes `1,234.50` or `1234.5` and stores `1234.5`; a date field `YYYY-MM-DD`; a choice field one of its options). `null` clears a value; keys you leave out keep theirs. One value that doesn't fit fails the whole request (422, every problem in `details.issues`).","type":"object","additionalProperties":true}},"required":["phone_number"]},"example":{"phone_number":"+14155551234","first_name":"Jane","last_name":"Smith","email":"jane@example.com","company":"Tech Startup","tags":["prospect","inbound"],"fields":{"interes":"Departamento"}}}}},"responses":{"201":{"description":"The contact (`source` is `api`).","content":{"application/json":{"example":{"success":true,"data":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+14155551234","first_name":"Jane","last_name":"Smith","display_name":null,"email":"jane@example.com","company":"Tech Startup","notes":null,"tags":["prospect","inbound"],"source":"api","created_at":"2026-10-02T02:25:06.947918+00:00","updated_at":"2026-10-02T02:25:06.947918+00:00","fields":{"interes":"Departamento"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The phone number is not a complete international number (`details.phoneUnroutable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"`validation_error`: A contact with this phone number already exists. Find it with GET /v1/contacts?search=<number>.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error`: A value in `fields` doesn't fit its field, or names no field (`details.issues`). Nothing was created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contacts/{id}":{"get":{"operationId":"getContact","summary":"Get a contact","description":"Returns one contact, including `crm_ids` (its record ids in connected CRMs such as HubSpot or Kommo) and `fields` (its contact field values by key).\n\nRequires the `contacts:read` scope.","tags":["Contacts"],"x-required-scope":"contacts:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#getContact"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The contact.","content":{"application/json":{"example":{"success":true,"data":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+14155551234","first_name":"Jane","last_name":"Smith","display_name":null,"email":"jane@example.com","company":"Tech Startup","notes":null,"tags":["prospect","inbound"],"source":"api","do_not_contact_at":null,"first_contact_at":null,"last_contact_at":"2026-10-02T02:14:49.108017+00:00","created_at":"2026-10-02T02:25:06.947918+00:00","updated_at":"2026-10-02T02:25:06.947918+00:00","fields":{"interes":"Departamento","presupuesto":"2500000"},"crm_ids":{"hubspot":"12345"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No contact with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateContact","summary":"Update a contact","description":"Changes the fields you send. `do_not_contact: true` opts the person out of every campaign call and message (`do_not_contact_at` is set); `false` lifts it. A new phone number must be international, and is refused while the contact's history (calls, conversations, appointments, campaign outreach) is tied to the old one.\n\nRequires the `contacts:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Contacts"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#updateContact"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_number":{"description":"New E.164 number with the country code.","type":"string","maxLength":20},"first_name":{"description":"First name.","type":"string","maxLength":100},"last_name":{"description":"Last name.","type":"string","maxLength":100},"display_name":{"description":"Name to show instead of first + last.","type":"string","maxLength":200},"email":{"description":"Email address.","type":"string","format":"email"},"company":{"description":"Company.","type":"string","maxLength":200},"notes":{"description":"Internal notes.","type":"string","maxLength":2000},"tags":{"description":"Tags. On update the list replaces the current tags.","type":"array","items":{"description":"A tag.","type":"string"}},"do_not_contact":{"description":"true: never call or message this person from campaigns; false: allow again.","type":"boolean"},"confirm_phone_change":{"description":"Only for repairing a legacy contact whose stored phone identities disagree: confirms that the number you send is the right one.","type":"boolean"},"fields":{"description":"Contact field values by field key (GET /v1/contact-fields lists the keys), e.g. `{\"interes\": \"Departamento\"}`. A value is a string, number or boolean and must fit the field's type (a number field takes `1,234.50` or `1234.5` and stores `1234.5`; a date field `YYYY-MM-DD`; a choice field one of its options). `null` clears a value; keys you leave out keep theirs. One value that doesn't fit fails the whole request (422, every problem in `details.issues`).","type":"object","additionalProperties":true}}},"example":{"company":"Enterprise Corp","tags":["customer","enterprise"],"fields":{"presupuesto":2500000,"interes":null}}}}},"responses":{"200":{"description":"The updated contact, with `fields` (all its values after the change) when the request sent `fields`.","content":{"application/json":{"example":{"success":true,"data":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+14155551234","first_name":"Jane","last_name":"Smith","display_name":null,"email":"jane@example.com","company":"Enterprise Corp","notes":null,"tags":["customer","enterprise"],"source":"api","do_not_contact_at":null,"first_contact_at":null,"last_contact_at":"2026-10-02T02:14:49.108017+00:00","created_at":"2026-10-02T02:25:06.947918+00:00","updated_at":"2026-10-02T02:25:15.469118+00:00","fields":{"presupuesto":"2500000"}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateContact"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No contact with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The phone change is blocked by linked history (`details.phoneChangeBlocked`), another contact already has the number, or the contact changed meanwhile (retry).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error`: A value in `fields` doesn't fit its field, names no field or an archived one (`details.issues`). Nothing was changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteContact","summary":"Delete a contact","description":"Permanently deletes a contact that has no history. A contact with calls, conversations, appointments or campaign outreach cannot be deleted (409), so that history is never reassigned to another person; opt it out with `do_not_contact` instead.\n\nRequires the `contacts:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Contacts"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#deleteContact"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The contact was deleted.","content":{"application/json":{"example":{"success":true,"data":{"deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteContact"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No contact with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The contact has linked history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/exclusions":{"post":{"operationId":"addExclusions","summary":"Add numbers to the exclusion list","description":"Adds up to 1000 phone numbers to the workspace's exclusion list: no campaign calls or messages them again. The contacts that own each number are opted out (`do_not_contact_at` is set, the same as `do_not_contact: true`) and their pending campaign enrollments close; a number that is not a contact yet becomes a contact already opted out (`source` is `exclusion_list`). Numbers must be international (a `+` and the country code). Every number gets an answer, in the order sent: `excluded`, `already_excluded` (nothing changed), `own_number` (one of the workspace's own lines, never a contact), `duplicate` (the same number earlier in the request) or `invalid`. Sending the same numbers again changes nothing.\n\nRequires the `contacts:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nTo lift an opt-out, PATCH the contact with `do_not_contact: false`.","tags":["Contacts"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#addExclusions"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_numbers":{"description":"Numbers to exclude, 1 to 1000.","type":"array","minItems":1,"maxItems":1000,"items":{"description":"E.164 number with the country code, e.g. +528112345678.","type":"string"}}},"required":["phone_numbers"]},"example":{"phone_numbers":["+528112345678","+14155551234","8112345678"]}}}},"responses":{"200":{"description":"Each number's answer and the totals.","content":{"application/json":{"example":{"success":true,"data":{"excluded":2,"already_excluded":0,"created":1,"own_number":0,"invalid":1,"duplicate":0,"results":[{"phone_number":"+528112345678","e164":"+528112345678","status":"excluded","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","created":false},{"phone_number":"+14155551234","e164":"+14155551234","status":"excluded","contact_id":"c2b3c4d5-e6f7-4a81-9bac-1d2e3f4a5b6c","created":true},{"phone_number":"8112345678","e164":null,"status":"invalid","contact_id":null,"created":false}]}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contacts/{id}/lists":{"get":{"operationId":"listContactListsOfContact","summary":"List a contact's lists","description":"The workspace's contact lists the contact is a member of, by name, as the contact's page shows them.\n\nRequires the `contacts:read` scope.\n\nNeeds `contact_lists:read` as well as `contacts:read`.","tags":["Contacts"],"x-required-scope":"contacts:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#listContactListsOfContact"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The contact's lists.","content":{"application/json":{"example":{"success":true,"data":[{"id":"d2406224-509f-4177-9a5a-b50bee1e7590","name":"Q2 Prospects","description":"Imported prospect list","contact_count":2,"created_at":"2026-10-02T02:25:25.707964+00:00","updated_at":"2026-10-02T02:25:25.707964+00:00"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No contact with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contacts/bulk-tags":{"post":{"operationId":"bulkTagContacts","summary":"Tag many contacts","description":"Adds tags to up to 200 contacts, removes tags from them, or both, as the contacts board's bulk \"Add tag\" does: each contact keeps its other tags.\n\nRequires the `contacts:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nTags match exactly (\"VIP\" and \"vip\" are two tags). A tag both added and removed is removed.\n\nA key whose changes wait for approval holds this request until an owner or admin approves it.","tags":["Contacts"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#bulkTagContacts"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact_ids":{"description":"The contacts.","type":"array","minItems":1,"maxItems":200,"items":{"description":"A contact id.","type":"string","format":"uuid"}},"add":{"description":"Tags to add (trimmed, 1 to 60 characters each).","type":"array","maxItems":10,"items":{"description":"A tag.","type":"string","minLength":1,"maxLength":60}},"remove":{"description":"Tags to remove. Send at least one tag to add or remove.","type":"array","maxItems":10,"items":{"description":"A tag.","type":"string","minLength":1,"maxLength":60}}},"required":["contact_ids"]},"example":{"contact_ids":["9595b646-ab97-4485-b1b8-e8b58dfcdd19","eaa5bc4d-b54a-4cb2-bcf8-c1b523f716eb"],"add":["vip"],"remove":["prospect"]}}}},"responses":{"200":{"description":"Which contacts changed, which already had those tags, and the ids not found in the workspace.","content":{"application/json":{"example":{"success":true,"data":{"add":["vip"],"remove":["prospect"],"updated":["9595b646-ab97-4485-b1b8-e8b58dfcdd19"],"unchanged":["eaa5bc4d-b54a-4cb2-bcf8-c1b523f716eb"],"not_found":[]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"bulkTagContacts"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-rules":{"get":{"operationId":"getContactRules","summary":"Get the contact rules","description":"The rules every campaign of the workspace follows (Settings → Workspace → Campaign calls): after someone talks with your agent on a call, no campaign calls that number again for `cooldown_days` days (null: the rule is off), and `cooldown_action` says what a campaign does with a number still inside that period. The same campaign's retries and the callbacks people ask for still go out.\n\nRequires the `campaigns:read` scope.\n\n`cooldown_action`: `wait` calls the number once the period ends (it stays in the campaign); `skip` leaves it out of the campaign.","tags":["Contacts"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#getContactRules"},"parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The rules.","content":{"application/json":{"example":{"success":true,"data":{"cooldown_days":90,"cooldown_action":"wait"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateContactRules","summary":"Set the contact rules","description":"Sets the rules every campaign of the workspace follows, as Settings → Workspace → Campaign calls does. A changed period re-dates every number the rule holds in the workspace's campaigns.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change is made as the member who created the key, and a key whose changes wait for approval holds it until an owner or admin approves it.","tags":["Contacts"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#updateContactRules"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"cooldown_days":{"description":"Days no campaign calls a number again after someone talked with your agent on it; null turns the rule off.","type":["integer","null"],"minimum":30,"maximum":365},"cooldown_action":{"description":"What a campaign does with a number inside the period. Left out, it stays as it is.","type":"string","enum":["wait","skip"]}},"required":["cooldown_days"]},"example":{"cooldown_days":90,"cooldown_action":"wait"}}}},"responses":{"200":{"description":"The rules, as saved.","content":{"application/json":{"example":{"success":true,"data":{"cooldown_days":90,"cooldown_action":"wait"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateContactRules"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key can't change the workspace's settings (only owners and admins can), or has left the workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: The setting isn't available while an update is being deployed; retry in a few minutes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/{id}/touches":{"post":{"operationId":"recordContactTouch","summary":"Record where a contact came from","description":"Records a touch: the contact reached the business from an ad or a tagged link that NuTalk didn't see, such as your landing page's form or your CRM. Their later conversions take it as their first or last touch, and its click ids let the ad platforms match them. Part of Conversions: only while it is on for the workspace.\n\nRequires the `conversions:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nOnly the listed keys are kept: anything else in `click_ids`, `ad` or `utm` is refused, so no personal data rides along. One touch per Idempotency-Key.","tags":["Contacts"],"x-required-scope":"conversions:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#recordContactTouch"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"occurred_at":{"description":"When they arrived (now when left out): at most 365 days ago, never in the future.","type":"string","format":"date-time"},"click_ids":{"description":"The click ids, each up to 500 characters: `ctwa_clid`, `fbclid`, `fbc`, `fbp`, `gclid`, `gbraid`, `wbraid`, `ttclid`, `ttp`. One platform's per touch.","type":"object","additionalProperties":true},"platform":{"description":"The ad platform. Read from the click ids when they carry it; required for an `ad` without click ids.","type":"string","enum":["meta","google","tiktok"]},"ad":{"description":"What the platform said about the ad: `id`, `adset_id`, `campaign_id`, `form_id`, `page_id`, `placement`, `lead_id`, `ref`, `source_type`, `url`, `headline`.","type":"object","additionalProperties":true},"utm":{"description":"The link's UTMs, each up to 200 characters: `source`, `medium`, `campaign`, `term`, `content`.","type":"object","additionalProperties":true},"page_url":{"description":"The page they landed on (http or https). Its query and fragment are dropped.","type":"string","format":"uri","maxLength":2000}}},"example":{"occurred_at":"2026-10-24T09:03:44-06:00","click_ids":{"gclid":"example-gclid"},"ad":{"campaign_id":"21000000001"},"utm":{"source":"google","medium":"cpc","campaign":"primera-consulta"},"page_url":"https://clinica.example.com/promo?email=redacted"}}}},"responses":{"200":{"description":"This Idempotency-Key already recorded this touch, with these details.","content":{"application/json":{"example":{"success":true,"data":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","occurred_at":"2026-10-24T15:03:44+00:00","recorded_at":"2026-10-24T15:03:45.120+00:00","channel":"api","platform":"google","kind":"ad","click_ids":{"gclid":"example-gclid"},"ad":{"campaign_id":"21000000001"},"utm":{"source":"google","medium":"cpc","campaign":"primera-consulta"},"page_url":"https://clinica.example.com/promo"}}}}},"201":{"description":"The touch was recorded. `kind` is `ad` (click ids or an ad), `tagged` (UTMs only) or `direct`.","content":{"application/json":{"example":{"success":true,"data":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","occurred_at":"2026-10-24T15:03:44+00:00","recorded_at":"2026-10-24T15:03:45.120+00:00","channel":"api","platform":"google","kind":"ad","click_ids":{"gclid":"example-gclid"},"ad":{"campaign_id":"21000000001"},"utm":{"source":"google","medium":"cpc","campaign":"primera-consulta"},"page_url":"https://clinica.example.com/promo"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The click ids are of two platforms, or don't match `platform`, or an ad without click ids has no `platform`. `validation_error`: The Idempotency-Key header is missing, or isn't 1 to 150 letters, digits and . _ : - (`details.reason` `idempotency_key_required`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Conversions is off for the workspace (`details.reason` `conversions_off`): NuTalk switches it on per workspace during the beta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No contact with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The Idempotency-Key already recorded a touch with other details (`details.reason` `idempotency_key_reused`, `details.fields`, `details.touch_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: The workspace recorded 600 conversions and touches through the API in the last hour, or 60 in the last minute (on top of its general limit).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-fields":{"get":{"operationId":"listContactFields","summary":"List contact fields","description":"Lists the workspace's contact fields in their order: the custom data each contact can hold (Settings › Contact fields).\n\nRequires the `contacts:read` scope.\n\nA field's key is made from its label when it is created and never changes: accents are spelled out and every other character becomes `_` (\"Interés\" is `interes`, \"Código postal\" is `codigo_postal`). Labels that differ only by accents, case or punctuation name the same field.\n\nA workspace has at most 200 active fields. Archived fields keep their values and can be restored.","tags":["Contacts"],"x-required-scope":"contacts:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#listContactFields"},"parameters":[{"name":"include_archived","in":"query","required":false,"description":"`true` also lists archived fields.","schema":{"type":"string","enum":["true","false"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The fields.","content":{"application/json":{"example":{"success":true,"data":[{"id":"5a7c9e1b-3d5f-4a7b-9c1d-3e5f7a9b1c2d","key":"interes","label":"Interés","type":"choice","options":["Casa","Departamento","Terreno"],"position":0,"variable":"{{contact.interes}}","archived_at":null,"created_at":"2026-10-09T16:00:00.000000+00:00","updated_at":"2026-10-09T16:00:00.000000+00:00"}]}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createContactField","summary":"Create a contact field","description":"Adds a contact field. Its key comes from the label. A label whose key another field already has is that field (409).\n\nRequires the `contacts:write` scope.\n\nA field's key is made from its label when it is created and never changes: accents are spelled out and every other character becomes `_` (\"Interés\" is `interes`, \"Código postal\" is `codigo_postal`). Labels that differ only by accents, case or punctuation name the same field.\n\nA workspace has at most 200 active fields. Archived fields keep their values and can be restored.","tags":["Contacts"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#createContactField"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"description":"What people see, e.g. Interés.","type":"string","maxLength":80},"type":{"description":"What the values are (dates are written YYYY-MM-DD; a choice needs `options`).","type":"string","enum":["text","number","date","choice","email","phone","url"],"default":"text"},"options":{"description":"Choice fields only (required for them): the allowed values.","type":"array","maxItems":100,"items":{"description":"An option.","type":"string","maxLength":80}}},"required":["label"]},"example":{"label":"Interés","type":"choice","options":["Casa","Departamento","Terreno"]}}}},"responses":{"201":{"description":"The new field.","content":{"application/json":{"example":{"success":true,"data":{"id":"5a7c9e1b-3d5f-4a7b-9c1d-3e5f7a9b1c2d","key":"interes","label":"Interés","type":"choice","options":["Casa","Departamento","Terreno"],"position":0,"variable":"{{contact.interes}}","archived_at":null,"created_at":"2026-10-09T16:00:00.000000+00:00","updated_at":"2026-10-09T16:00:00.000000+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"`validation_error`: A field with the same key exists (`details.reason` `contact_field_exists`, or `contact_field_archived` when archived: restore it with PATCH), the label names a contact detail such as Email (`contact_field_reserved`), or the workspace has 200 active fields (`contact_fields_limit`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-fields/{id}":{"get":{"operationId":"getContactField","summary":"Get a contact field","description":"Returns one contact field, by id or by key.\n\nRequires the `contacts:read` scope.","tags":["Contacts"],"x-required-scope":"contacts:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#getContactField"},"parameters":[{"name":"id","in":"path","required":true,"description":"The field's id or key.","schema":{"type":"string"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The field.","content":{"application/json":{"example":{"success":true,"data":{"id":"5a7c9e1b-3d5f-4a7b-9c1d-3e5f7a9b1c2d","key":"interes","label":"Interés","type":"choice","options":["Casa","Departamento","Terreno"],"position":0,"variable":"{{contact.interes}}","archived_at":null,"created_at":"2026-10-09T16:00:00.000000+00:00","updated_at":"2026-10-09T16:00:00.000000+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No contact field with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateContactField","summary":"Update a contact field","description":"Renames (the key stays), retypes, reorders, archives or restores a field, or changes a choice field's options. A new type must fit every value contacts already have (they are rewritten that type's way; past 10,000 values a field only changes to text), and a choice field keeps every option in use.\n\nRequires the `contacts:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Contacts"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contacts#updateContactField"},"parameters":[{"name":"id","in":"path","required":true,"description":"The field's id or key.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"description":"New label.","type":"string","maxLength":80},"type":{"description":"New type.","type":"string","enum":["text","number","date","choice","email","phone","url"]},"options":{"description":"Choice fields: the allowed values (they replace the current list).","type":"array","maxItems":100,"items":{"description":"An option.","type":"string","maxLength":80}},"position":{"description":"Place in the list (0 first).","type":"integer","minimum":0},"archived":{"description":"true archives the field (values kept); false restores it.","type":"boolean"}}},"example":{"label":"Tipo de propiedad"}}}},"responses":{"200":{"description":"The field.","content":{"application/json":{"example":{"success":true,"data":{"id":"5a7c9e1b-3d5f-4a7b-9c1d-3e5f7a9b1c2d","key":"interes","label":"Tipo de propiedad","type":"choice","options":["Casa","Departamento","Terreno"],"position":0,"variable":"{{contact.interes}}","archived_at":null,"created_at":"2026-10-09T16:00:00.000000+00:00","updated_at":"2026-10-09T16:00:00.000000+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateContactField"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No contact field with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: Some values don't fit the new type or options (`details.reason` `contact_field_values_incompatible`, with `count` and `examples`), the field has more than 10,000 values (`contact_field_too_many_values`), or restoring it would pass 200 active fields (`contact_fields_limit`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-lists":{"get":{"operationId":"listContactLists","summary":"List contact lists","description":"Lists contact lists, newest first, with their member counts.\n\nRequires the `contact_lists:read` scope.","tags":["Contact lists"],"x-required-scope":"contact_lists:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-lists#listContactLists"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of lists.","content":{"application/json":{"example":{"success":true,"data":[{"id":"d2406224-509f-4177-9a5a-b50bee1e7590","name":"Q2 Prospects","description":"Imported prospect list","contact_count":2,"created_at":"2026-10-02T02:25:25.707964+00:00","updated_at":"2026-10-02T02:25:25.707964+00:00"}],"pagination":{"total":1,"limit":10,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createContactList","summary":"Create a contact list","description":"Creates an empty list. Add members with `POST /v1/contact-lists/{id}/members` or a contact import (`POST /v1/contact-imports`); in the dashboard, CRM syncs and campaign imports fill lists too.\n\nRequires the `contact_lists:write` scope.","tags":["Contact lists"],"x-required-scope":"contact_lists:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-lists#createContactList"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"List name.","type":"string","minLength":1,"maxLength":200},"description":{"description":"What the list holds.","type":"string","maxLength":1000}},"required":["name"]},"example":{"name":"Q2 Prospects","description":"Imported prospect list"}}}},"responses":{"201":{"description":"The list.","content":{"application/json":{"example":{"success":true,"data":{"id":"d2406224-509f-4177-9a5a-b50bee1e7590","name":"Q2 Prospects","description":"Imported prospect list","contact_count":0,"created_at":"2026-10-02T02:25:25.707964+00:00","updated_at":"2026-10-02T02:25:25.707964+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-lists/{id}":{"get":{"operationId":"getContactList","summary":"Get a contact list","description":"Returns one list. With `include_members=true` it also returns a page of its members (newest first) with their contact details.\n\nRequires the `contact_lists:read` scope.\n\nMembers come with their name, phone and email, so a key with `contact_lists:read` can read those contacts even without `contacts:read`.","tags":["Contact lists"],"x-required-scope":"contact_lists:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-lists#getContactList"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact list id.","schema":{"type":"string","format":"uuid"}},{"name":"include_members","in":"query","required":false,"description":"Also return a page of members.","schema":{"type":"boolean","default":false}},{"name":"members_limit","in":"query","required":false,"description":"Members per page.","schema":{"type":"integer","default":25,"minimum":1,"maximum":100}},{"name":"members_offset","in":"query","required":false,"description":"Members to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The list, with `members` when requested.","content":{"application/json":{"example":{"success":true,"data":{"id":"d2406224-509f-4177-9a5a-b50bee1e7590","name":"Q2 Prospects","description":"Imported prospect list","contact_count":2,"created_at":"2026-10-02T02:25:25.707964+00:00","updated_at":"2026-10-02T02:25:25.707964+00:00","members":{"data":[{"id":"c1a2b3c4-d5e6-4f70-8a9b-0c1d2e3f4a5b","added_at":"2026-10-02T02:30:00.000000+00:00","contacts":{"id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+14155551234","first_name":"Jane","last_name":"Smith","display_name":null,"email":"jane@example.com","company":"Tech Startup","tags":["prospect"],"created_at":"2026-10-02T02:25:06.947918+00:00","updated_at":"2026-10-02T02:25:06.947918+00:00"}}],"pagination":{"total":2,"limit":25,"offset":0,"has_more":false}}}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No contact list with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateContactList","summary":"Update a contact list","description":"Renames a list or changes its description (`null` clears it).\n\nRequires the `contact_lists:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Contact lists"],"x-required-scope":"contact_lists:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-lists#updateContactList"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact list id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"New name.","type":"string","minLength":1,"maxLength":200},"description":{"description":"New description, or null.","type":["string","null"],"maxLength":1000}}},"example":{"name":"Q2 Enterprise Prospects"}}}},"responses":{"200":{"description":"The updated list.","content":{"application/json":{"example":{"success":true,"data":{"id":"d2406224-509f-4177-9a5a-b50bee1e7590","name":"Q2 Enterprise Prospects","description":"Imported prospect list","contact_count":2,"created_at":"2026-10-02T02:25:25.707964+00:00","updated_at":"2026-10-02T02:25:26.692147+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateContactList"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No contact list with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteContactList","summary":"Delete a contact list","description":"Deletes the list and its memberships. The contacts themselves stay; a campaign that used it as its default list keeps running without one. The enrollments the list created in its campaigns are removed while they have not produced an outcome yet, so when the list is linked to a campaign the key also needs `campaigns:write`.\n\nRequires the `contact_lists:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Contact lists"],"x-required-scope":"contact_lists:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-lists#deleteContactList"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact list id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The list was deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"d2406224-509f-4177-9a5a-b50bee1e7590","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteContactList"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The list is linked to a campaign and the key lacks `campaigns:write` (`details.required_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No contact list with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-lists/{id}/members":{"post":{"operationId":"addContactListMembers","summary":"Add contacts to a list","description":"Adds existing contacts of your workspace to the list (create them first with POST /v1/contacts). A contact already on the list is left as it is, and an id that is not one of your contacts is reported in `not_found`, never added. Adding a contact to a list that an outbound campaign uses enrolls it in that campaign, as in the dashboard: an active campaign starts contacting it. So when the list feeds an outbound campaign (draft, paused or active), the key also needs `campaigns:write`, the scope that enrolls contacts.\n\nRequires the `contact_lists:write` scope.\n\nDo-not-contact contacts join the list but are never enrolled in its campaigns.\n\nPlacing contacts on a list used by an active campaign reaches real people and spends credits: confirm with the user first.","tags":["Contact lists"],"x-required-scope":"contact_lists:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-lists#addContactListMembers"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact list id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact_ids":{"description":"Contacts to add, 1 to 500 ids.","type":"array","minItems":1,"maxItems":500,"items":{"description":"A contact id.","type":"string","format":"uuid"}}},"required":["contact_ids"]},"example":{"contact_ids":["9595b646-ab97-4485-b1b8-e8b58dfcdd19"]}}}},"responses":{"200":{"description":"What was added.","content":{"application/json":{"example":{"success":true,"data":{"list_id":"d2406224-509f-4177-9a5a-b50bee1e7590","added":1,"already_members":0,"not_found":[],"contact_count":3}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: An entry of `contact_ids` is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The list feeds an outbound campaign and the key lacks `campaigns:write` (`details.required_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No contact list with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-lists/{id}/members/{contact_id}":{"delete":{"operationId":"removeContactListMember","summary":"Remove a contact from a list","description":"Removes one contact from the list; the contact itself stays. The enrollments this list created in its campaigns are removed while they have not produced an outcome yet; calls already made and their outcomes stay. When the list is linked to a campaign, the key also needs `campaigns:write`.\n\nRequires the `contact_lists:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Contact lists"],"x-required-scope":"contact_lists:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-lists#removeContactListMember"},"parameters":[{"name":"id","in":"path","required":true,"description":"The contact list id.","schema":{"type":"string","format":"uuid"}},{"name":"contact_id","in":"path","required":true,"description":"The contact id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The contact was removed from the list.","content":{"application/json":{"example":{"success":true,"data":{"list_id":"d2406224-509f-4177-9a5a-b50bee1e7590","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","removed":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"removeContactListMember"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The list is linked to a campaign and the key lacks `campaigns:write` (`details.required_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No contact list with this id in your workspace (ids of other workspaces answer the same). `not_found`: The contact is not on this list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-imports":{"post":{"operationId":"createContactImport","summary":"Create an import","description":"Starts a background import of a CSV file into a campaign (its duplicates rule applies) or a contact list, and answers one signed upload URL per part of the file. PUT each part's bytes to its URL (`upload_url` is the first part's; a file up to 32 MiB has only that one), then call POST /v1/contact-imports/{id}/start. Every number is checked as in the dashboard: Mexico, the United States and Canada, a mobile or landline.\n\nRequires the `contacts:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nUpload each part with `PUT <url>`, the part's bytes (`byte_start` up to `byte_end`, exclusive) as the body and `Content-Type: text/csv`. A part uploaded again replaces itself.\n\nThe URLs are valid for two hours, and replaying this call with the same `Idempotency-Key` answers the same URLs: when an upload takes longer, POST /v1/contact-imports/{id}/upload answers fresh ones for the parts still missing.\n\nThe first row is the header. Values may be quoted, span lines and use \"\" for a quote; rows of empty cells are skipped. Row numbers count as a spreadsheet shows them (the header is row 1).\n\nA number repeated in the file is imported once (its first row). For a campaign, a person already enrolled follows its duplicates rule: `skip`, `update_only` (the lead data is replaced) or `re_enroll` (enrolled again; a person who is being called or has a callback booked keeps their place, and only their lead data is replaced).\n\nThe campaign dials while the import runs: people of an active campaign are queued as their rows land; a draft or paused campaign holds them until it starts.","tags":["Contact imports"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-imports#createContactImport"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"description":"Import into this campaign (or send `contact_list_id`).","type":"string","format":"uuid"},"contact_list_id":{"description":"Import into this list (or send `campaign_id`).","type":"string","format":"uuid"},"file_name":{"description":"The file's name, shown in the dashboard.","type":"string","maxLength":255},"file_size":{"description":"The file's exact size in bytes (it decides the parts: 32 MiB each).","type":"integer","format":"int64","minimum":1,"maximum":4294967296},"columns":{"description":"Which column holds what. Only `phone` is required.","type":"object","properties":{"phone":{"description":"The phone number. A column of your file: its header text (`\"Teléfono\"`, matched without case or accents), its zero-based position (`0`), or `{\"index\": 0, \"header\": \"Teléfono\"}`.","type":"string"},"name":{"description":"Full name. A column of your file: its header text (`\"Teléfono\"`, matched without case or accents), its zero-based position (`0`), or `{\"index\": 0, \"header\": \"Teléfono\"}`.","type":"string"},"first_name":{"description":"First name. A column of your file: its header text (`\"Teléfono\"`, matched without case or accents), its zero-based position (`0`), or `{\"index\": 0, \"header\": \"Teléfono\"}`.","type":"string"},"last_name":{"description":"Last name. A column of your file: its header text (`\"Teléfono\"`, matched without case or accents), its zero-based position (`0`), or `{\"index\": 0, \"header\": \"Teléfono\"}`.","type":"string"},"email":{"description":"Email (an invalid one is dropped). A column of your file: its header text (`\"Teléfono\"`, matched without case or accents), its zero-based position (`0`), or `{\"index\": 0, \"header\": \"Teléfono\"}`.","type":"string"},"company":{"description":"Company (fills the contact's when empty). A column of your file: its header text (`\"Teléfono\"`, matched without case or accents), its zero-based position (`0`), or `{\"index\": 0, \"header\": \"Teléfono\"}`.","type":"string"},"notes":{"description":"Notes for the agent. A column of your file: its header text (`\"Teléfono\"`, matched without case or accents), its zero-based position (`0`), or `{\"index\": 0, \"header\": \"Teléfono\"}`.","type":"string"},"external_id":{"description":"Your id for the person, kept on the contact as `crm_ids[source]`. A column of your file: its header text (`\"Teléfono\"`, matched without case or accents), its zero-based position (`0`), or `{\"index\": 0, \"header\": \"Teléfono\"}`.","type":"string"},"custom":{"description":"Columns kept as custom fields: in the enrollment's lead data under `key`, and as fields of the campaign's contact list (or of the list) that workflow conditions can read.","type":"array","maxItems":200,"items":{"description":"A custom column.","type":"object","properties":{"column":{"description":"A column of your file: its header text (`\"Teléfono\"`, matched without case or accents), its zero-based position (`0`), or `{\"index\": 0, \"header\": \"Teléfono\"}`.","type":"string"},"key":{"description":"Its key; the header when omitted.","type":"string","maxLength":200},"display_name":{"description":"What the list shows; the header when omitted.","type":"string","maxLength":200}},"required":["column"]}}},"required":["phone"]},"phone_country":{"description":"The country of numbers written without their country code: `MX` (+52) or `US` (+1, the US and Canada). Without it they are read in the country your workspace or the campaign's caller ID says, except a number a contact already holds, with or without the code. Those rows, and all of them where nothing says, are refused, never guessed.","type":["string","null"],"enum":["MX","US",null]},"delimiter":{"description":"The separator; detected from the file when omitted.","type":["string","null"],"enum":[",",";","\t","|",null]},"encoding":{"description":"The text encoding; detected when omitted (Excel's \"CSV\" in Spanish is windows-1252).","type":["string","null"],"enum":["utf-8","windows-1252",null]},"source":{"description":"Origin label for the enrollments (lowercase letters, digits, - and _, up to 40), and the key of `external_id`. Platform labels are refused, as in /campaigns/{id}/contacts/import.","type":"string","default":"api"}},"required":["file_name","file_size","columns"]},"example":{"campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","file_name":"base-octubre.csv","file_size":148210544,"phone_country":"MX","columns":{"phone":"Teléfono","name":"Nombre","email":"Correo","custom":[{"column":"Interés","key":"interes"}]}}}}},"responses":{"201":{"description":"The import, awaiting its upload, with the URL of every part (valid for two hours).","content":{"application/json":{"example":{"success":true,"data":{"id":"3c9a0f7e-5d21-4b8e-9a47-6f1e2d3c4b5a","status":"awaiting_upload","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","contact_list_id":null,"file_name":"base-octubre.csv","file_size":148210544,"progress":{"bytes_processed":0,"percent":0,"estimated_total_rows":null},"rows":{"processed":0,"added":0,"updated":0,"reenrolled":0,"duplicates":0,"rejected":0},"rejected_by_reason":{},"rejects_available":0,"contacts_created":0,"error":null,"created_at":"2026-10-07T15:02:11.512Z","started_at":null,"completed_at":null,"upload_url":"https://api.nutalk.ai/storage/v1/object/upload/sign/contact-imports/…/part-0001.csv?token=…","upload":{"method":"PUT","part_size":33554432,"expires_at":"2026-10-07T17:02:11.512Z","parts":[{"part":1,"url":"https://api.nutalk.ai/storage/v1/object/upload/sign/contact-imports/…/part-0001.csv?token=…","byte_start":0,"byte_end":33554432},{"part":2,"url":"https://api.nutalk.ai/storage/v1/object/upload/sign/contact-imports/…/part-0002.csv?token=…","byte_start":33554432,"byte_end":67108864}]}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key lacks `campaigns:write` (a campaign, or a list that feeds an outbound campaign) or `contact_lists:write` (a list).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No campaign or contact list with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`campaign_not_active`: The campaign is not active and API import is off in its settings (turn it on to hold the people until it starts). `campaign_not_accepting`: The campaign is completed or archived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-imports/{id}/upload":{"post":{"operationId":"renewContactImportUpload","summary":"Renew the upload URLs","description":"Fresh upload URLs (valid for two hours) for the parts of the file not uploaded yet, or uploaded with another size. Parts already in place keep their bytes.\n\nRequires the `contacts:write` scope.","tags":["Contact imports"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-imports#renewContactImportUpload"},"parameters":[{"name":"id","in":"path","required":true,"description":"The import id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The import, awaiting its upload, with a URL for every part still missing.","content":{"application/json":{"example":{"success":true,"data":{"id":"3c9a0f7e-5d21-4b8e-9a47-6f1e2d3c4b5a","status":"awaiting_upload","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","contact_list_id":null,"file_name":"base-octubre.csv","file_size":148210544,"progress":{"bytes_processed":0,"percent":0,"estimated_total_rows":null},"rows":{"processed":0,"added":0,"updated":0,"reenrolled":0,"duplicates":0,"rejected":0},"rejected_by_reason":{},"rejects_available":0,"contacts_created":0,"error":null,"created_at":"2026-10-07T15:02:11.512Z","started_at":null,"completed_at":null,"upload_url":"https://api.nutalk.ai/storage/v1/object/upload/sign/contact-imports/…/part-0002.csv?token=…","upload":{"method":"PUT","part_size":33554432,"expires_at":"2026-10-07T19:05:40.208Z","parts":[{"part":2,"url":"https://api.nutalk.ai/storage/v1/object/upload/sign/contact-imports/…/part-0002.csv?token=…","byte_start":33554432,"byte_end":67108864}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No import with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`already_uploaded`: The import already started (or was cancelled): its file needs no upload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-imports/{id}/start":{"post":{"operationId":"startContactImport","summary":"Start an import","description":"Call it once every part is uploaded: the import starts in the background (about 2,000,000 rows in under two hours). Starting an import that already started answers it as it is.\n\nRequires the `contacts:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Contact imports"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-imports#startContactImport"},"parameters":[{"name":"id","in":"path","required":true,"description":"The import id.","schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"The import, queued.","content":{"application/json":{"example":{"success":true,"data":{"id":"3c9a0f7e-5d21-4b8e-9a47-6f1e2d3c4b5a","status":"queued","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","contact_list_id":null,"file_name":"base-octubre.csv","file_size":148210544,"progress":{"bytes_processed":61340112,"percent":41,"estimated_total_rows":2000480},"rows":{"processed":827950,"added":801204,"updated":0,"reenrolled":0,"duplicates":9112,"rejected":17634},"rejected_by_reason":{"invalid_phone":16920,"missing_phone":410,"do_not_contact":304},"rejects_available":17634,"contacts_created":786015,"error":null,"created_at":"2026-10-07T15:02:11.512Z","started_at":"2026-10-07T15:04:40.031Z","completed_at":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No import with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`upload_incomplete`: A part is missing or has another size (`details.missing_parts`). Upload it and call again. `campaign_not_accepting`: The campaign was completed or archived since the import was created. `campaign_not_active`: The campaign is no longer active and API import is off in its settings (it was paused, or the setting turned off, during the upload). `not_startable`: The import was cancelled or failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-imports/{id}":{"get":{"operationId":"getContactImport","summary":"Get an import's progress","description":"Status, bytes read, and what happened to the rows so far. Poll it every few seconds while it runs.\n\nRequires the `contacts:read` scope.\n\nThe fields of an import:\n\n`id`: The import id.\n\n`status`: `awaiting_upload` until you call /start; `queued` then `processing` while it runs; `paused` from the dashboard; `completed`, `failed` (see `error`) or `cancelled`.\n\n`campaign_id`: The campaign the rows join.\n\n`contact_list_id`: The list the rows join.\n\n`file_name`: The name you gave the file.\n\n`file_size`: The file's size in bytes.\n\n`progress`: How far the import got.\n\n`rows`: Every row lands in one of added, updated, duplicates or rejected.\n\n`rejected_by_reason`: Refused rows by reason: `missing_phone`, `invalid_phone` (not a Mexican, US or Canadian mobile or landline, or no country code with no country to read it in, or a contact already holds it), `agent_number` (one of your own numbers), `do_not_contact` (the person asked not to be contacted), `cooldown`.\n\n`rejects_available`: Refused rows you can download (the first 250,000).\n\n`contacts_created`: Contacts the import created (the rest already existed).\n\n`error`: Why a failed import stopped (`code`, `message`); while it retries, the last error. `campaign_scope`: the list came to feed an outbound campaign during the import, and the key that started it lacks `campaigns:write`.\n\n`created_at`: When the import was created.\n\n`started_at`: When processing started.\n\n`completed_at`: When it finished.","tags":["Contact imports"],"x-required-scope":"contacts:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-imports#getContactImport"},"parameters":[{"name":"id","in":"path","required":true,"description":"The import id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The import.","content":{"application/json":{"example":{"success":true,"data":{"id":"3c9a0f7e-5d21-4b8e-9a47-6f1e2d3c4b5a","status":"processing","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","contact_list_id":null,"file_name":"base-octubre.csv","file_size":148210544,"progress":{"bytes_processed":61340112,"percent":41,"estimated_total_rows":2000480},"rows":{"processed":827950,"added":801204,"updated":0,"reenrolled":0,"duplicates":9112,"rejected":17634},"rejected_by_reason":{"invalid_phone":16920,"missing_phone":410,"do_not_contact":304},"rejects_available":17634,"contacts_created":786015,"error":null,"created_at":"2026-10-07T15:02:11.512Z","started_at":"2026-10-07T15:04:40.031Z","completed_at":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No import with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-imports/{id}/cancel":{"post":{"operationId":"cancelContactImport","summary":"Cancel an import","description":"Stops the import for good and deletes its file. Rows already imported stay imported (enrolled people may already be called). Cancelling a cancelled import answers it as it is.\n\nRequires the `contacts:write` scope.","tags":["Contact imports"],"x-required-scope":"contacts:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-imports#cancelContactImport"},"parameters":[{"name":"id","in":"path","required":true,"description":"The import id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The cancelled import.","content":{"application/json":{"example":{"success":true,"data":{"id":"3c9a0f7e-5d21-4b8e-9a47-6f1e2d3c4b5a","status":"cancelled","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","contact_list_id":null,"file_name":"base-octubre.csv","file_size":148210544,"progress":{"bytes_processed":61340112,"percent":41,"estimated_total_rows":2000480},"rows":{"processed":827950,"added":801204,"updated":0,"reenrolled":0,"duplicates":9112,"rejected":17634},"rejected_by_reason":{"invalid_phone":16920,"missing_phone":410,"do_not_contact":304},"rejects_available":17634,"contacts_created":786015,"error":null,"created_at":"2026-10-07T15:02:11.512Z","started_at":"2026-10-07T15:04:40.031Z","completed_at":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No import with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`not_cancellable`: The import already completed or failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/contact-imports/{id}/rejects":{"get":{"operationId":"getContactImportRejects","summary":"Download the refused rows","description":"The rows the import did not take, as CSV: your file's columns and separator, each row as written, then why it was not imported and its row number. Fix them and import the file again.\n\nRequires the `contacts:read` scope.","tags":["Contact imports"],"x-required-scope":"contacts:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/contact-imports#getContactImportRejects"},"parameters":[{"name":"id","in":"path","required":true,"description":"The import id.","schema":{"type":"string","format":"uuid"}},{"name":"locale","in":"query","required":false,"description":"The language of the reason column.","schema":{"type":"string","enum":["en","es"],"default":"en"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A CSV file (UTF-8 with a byte order mark)."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No import with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns":{"get":{"operationId":"listCampaigns","summary":"List campaigns","description":"Lists campaigns, newest first, archived ones included unless you filter by status. `completed_enrollments` counts every enrollment that finished (completed, failed, exhausted, rejected or removed); `goal_reached_count` counts the completed ones whose lead response is `interested` or `appointment_booked`.\n\nRequires the `campaigns:read` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#listCampaigns"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only campaigns in this status.","schema":{"type":"string","enum":["draft","active","paused","completed","archived"]}},{"name":"type","in":"query","required":false,"description":"Only campaigns of this type.","schema":{"type":"string","enum":["outbound","inbound"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of campaigns.","content":{"application/json":{"example":{"success":true,"data":[{"id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","name":"Summer Sale Campaign","description":"Outreach for summer promotion","type":"outbound","status":"draft","total_enrollments":0,"active_enrollments":0,"completed_enrollments":0,"goal_reached_count":0,"business_hours":{"enabled":true,"timezone":"America/Los_Angeles","schedule":{"monday":{"enabled":true,"start":"10:00","end":"18:00"},"tuesday":{"enabled":true,"start":"10:00","end":"18:00"},"wednesday":{"enabled":true,"start":"10:00","end":"18:00"}},"holidays":[]},"rate_limits":{"max_concurrent_calls":5,"max_calls_per_hour":50,"max_calls_per_lead_per_day":3,"min_minutes_between_attempts":30},"created_at":"2026-10-02T02:26:06.132516+00:00","updated_at":"2026-10-02T02:26:06.132516+00:00"}],"pagination":{"total":1,"limit":10,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createCampaign","summary":"Create a campaign","description":"Creates a `draft` campaign. With `purpose` or `agent_id` (and `outbound_channel_id`, which they need), its steps are the ones the dashboard builds once the calling agent is chosen: call the contact from the outbound channel, check whether the call was answered, have the AI read the call, and end it as a success or not by the purpose's question (an unanswered call ends as \"Didn't answer\"). Without either, the older default: call, then mark the enrollment `interested` when the call was answered or `not_interested` when it was not. Calls retry per the campaign's retry settings (`retries`) before the outcome is taken.\n\nRequires the `campaigns:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nRead or replace the steps later with GET and PUT /v1/campaigns/{id}/workflow: another preset, or a whole workflow (several numbers, waits, WhatsApp templates, CRM steps, switching agents).\n\n`rate_limits.min_minutes_between_attempts` is stored and returned, and it is not applied: the campaign engine never reads it. How long a campaign waits before it calls the same contact again is set by its steps and its retry settings (`retries`). The cap that limits calls to one contact is `max_calls_per_lead_per_day`.\n\nA campaign skips contacts already enrolled in it unless `deduplication` says otherwise. Enrollments to a draft or paused campaign answer 409 unless `api_import_enabled` is true: then contacts wait (`status: enrolled`) and start when the campaign does. Linking a contact list (PUT /v1/campaigns/{id}/contact-lists/{list_id}) holds its members the same way.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#createCampaign"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Campaign name.","type":"string","minLength":1,"maxLength":200},"description":{"description":"What the campaign is for.","type":"string","maxLength":1000},"type":{"description":"outbound calls the contacts you enroll; inbound handles leads that arrive from integrations.","type":"string","enum":["outbound","inbound"],"default":"outbound"},"outbound_channel_id":{"description":"The voice channel (phone number) the campaign calls from; it must be one of your channels with a deployed agent. Required before an outbound campaign can start.","type":"string","format":"uuid"},"business_hours":{"description":"When the campaign may call or message. Days not listed are closed. Omitted on create: Monday to Friday 09:00-18:00 (Friday until 17:00), America/Monterrey.","type":"object","properties":{"timezone":{"description":"IANA timezone the hours are in, e.g. America/Mexico_City.","type":"string"},"schedule":{"description":"Open days keyed by weekday (monday, tuesday, wednesday, thursday, friday, saturday, sunday), each {start, end}. `days` is accepted as an older name.","type":"object","properties":{"monday":{"description":"Monday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"tuesday":{"description":"Tuesday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"wednesday":{"description":"Wednesday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"thursday":{"description":"Thursday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"friday":{"description":"Friday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"saturday":{"description":"Saturday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"sunday":{"description":"Sunday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]}}},"holidays":{"description":"Dates (YYYY-MM-DD, in the campaign timezone) the campaign does not call.","type":"array","items":{"description":"A date.","type":"string"}},"enabled":{"description":"false keeps the schedule but lets the campaign call at any hour.","type":"boolean","default":true}},"required":["timezone"]},"rate_limits":{"description":"Caps. Omitted: 5 simultaneous calls, 50 calls per hour and 3 calls per contact per day (on a plan that allows fewer simultaneous or hourly calls, the plan's numbers). Sent: `max_concurrent_calls` is stored with the 3 calls per contact per day and the 50 calls per hour are not applied, so leave it out unless you want exactly that. Either way the campaign is also stored with `min_minutes_between_attempts: 30`, which is not applied (see the notes).","type":"object","properties":{"max_concurrent_calls":{"description":"Simultaneous calls (also capped by your plan).","type":"integer","minimum":1,"maximum":100}}},"purpose":{"description":"What counts as success: the AI reads each answered call and the campaign ends it as a success when the answer to the purpose's question is yes. `general` (Did the person show clear interest in continuing?); `lead_qualification` (Did the person show clear interest in continuing?); `appointment_booking` (Was an appointment booked with a confirmed date and time?); `appointment_confirmation` (Did the person confirm the appointment?); `payment_reminder` (Did the person commit to paying (with a date if given)?); `reactivation` (Did the person show clear interest in continuing?); `customer_survey` (Did the person answer the survey questions?). `general` is what the dashboard builds.","type":"string","enum":["general","lead_qualification","appointment_booking","appointment_confirmation","payment_reminder","reactivation","customer_survey"]},"agent_id":{"description":"The agent that makes the calls, named on the call step: one of yours, not archived, with a deployed version. Without it the agent deployed on `outbound_channel_id` talks, as in the dashboard.","type":"string","format":"uuid"},"step_names_language":{"description":"The language the steps are named in on the dashboard.","type":"string","enum":["en","es"],"default":"en"},"api_import_enabled":{"description":"true: contacts you enroll or import while the campaign is a draft or paused wait (`status: enrolled`) and are called once it starts, so you can load them before the start; false (the default): those requests answer 409 until the campaign is active. The dashboard calls it API import.","type":"boolean"},"deduplication":{"description":"What enrolling someone already in the campaign does: `skip` (the default) leaves them as they are; `re_enroll` starts them over at the first step; `update_only` updates their details and keeps their progress.","type":"string","enum":["skip","re_enroll","update_only"]},"retries":{"description":"Calling again a contact who didn't answer, got voicemail, was busy or whose call failed (Settings → Retries, simple mode). Sent, it replaces the retry settings: a field you leave out keeps its stored value, and retries set in the dashboard's advanced mode become simple ones. Omitted on create: 2 retries (3 calls in all), the first an hour after the call and the next twice as late, as a campaign made in the dashboard starts.","type":"object","properties":{"enabled":{"description":"false: each contact gets one call.","type":"boolean"},"max_retries":{"description":"Calls after the first one.","type":"integer","minimum":1,"maximum":10},"delay_minutes":{"description":"Minutes before the first retry: one of 5, 15, 30, 60, 120, 240, 1440.","type":"integer","minimum":5,"maximum":1440},"exponential_backoff":{"description":"true: each next retry waits twice as long as the one before.","type":"boolean"}},"required":["enabled"]},"callbacks_enabled":{"description":"true: when someone says \"call me later\", the agent asks when, books it, and the campaign calls back at that time (pending retries wait for the callback). false (the default): such requests are not scheduled.","type":"boolean"},"max_call_seconds":{"description":"The longest one call may last, in whole minutes written as seconds (60 to 7200, a multiple of 60): the carrier ends the call there. null (the default): no limit. Calls from an AiBox line or Zoom Phone don't take it.","type":["integer","null"],"minimum":60,"maximum":7200}},"required":["name"]},"example":{"name":"Citas de octubre","description":"Call October's leads to book a first appointment","type":"outbound","outbound_channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","purpose":"appointment_booking","agent_id":"550e8400-e29b-41d4-a716-446655440000","step_names_language":"es","business_hours":{"timezone":"America/Monterrey","schedule":{"monday":{"start":"10:00","end":"18:00"},"tuesday":{"start":"10:00","end":"18:00"},"wednesday":{"start":"10:00","end":"18:00"}}},"api_import_enabled":true,"retries":{"enabled":true,"max_retries":2,"delay_minutes":120}}}}},"responses":{"201":{"description":"The draft campaign.","content":{"application/json":{"example":{"success":true,"data":{"id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","name":"Summer Sale Campaign","description":"Outreach for summer promotion","type":"outbound","status":"draft","total_enrollments":0,"active_enrollments":0,"completed_enrollments":0,"goal_reached_count":0,"business_hours":{"enabled":true,"timezone":"America/Los_Angeles","schedule":{"monday":{"enabled":true,"start":"10:00","end":"18:00"},"tuesday":{"enabled":true,"start":"10:00","end":"18:00"},"wednesday":{"enabled":true,"start":"10:00","end":"18:00"}},"holidays":[]},"rate_limits":{"max_concurrent_calls":5,"max_calls_per_hour":50,"max_calls_per_lead_per_day":3,"min_minutes_between_attempts":30},"created_at":"2026-10-02T02:26:06.132516+00:00","updated_at":"2026-10-02T02:26:06.132516+00:00","outbound_channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","api_import_enabled":false,"deduplication":"skip","callbacks_enabled":false,"max_call_seconds":null,"retries":{"enabled":true,"mode":"simple","max_retries":2,"delay_minutes":60,"exponential_backoff":true}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `outbound_channel_id` is not one of your channels (with `purpose` or `agent_id`, it is required and must be one of your voice channels), `agent_id` is not one of your agents, is archived or has no deployed version, or `business_hours` is invalid (unknown timezone, a time not in HH:MM, no open day).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}":{"get":{"operationId":"getCampaign","summary":"Get a campaign","description":"Returns one campaign with its counters, settings (calling hours, caps, API import, duplicates rule, retries, callbacks, longest call), outbound channel, the contact lists linked to it (`contact_lists`, oldest link first; `is_default` marks the one its imports and analysis fields go to), default contact list and inbound webhook URL. `retries.mode` is `advanced` when the retries were set in the dashboard's advanced mode (time windows, stopping rules); only `enabled` is shown then.\n\nRequires the `campaigns:read` scope.\n\n`webhook_url` receives leads from forms and ad platforms for this campaign; each request must carry the campaign's webhook secret, shown in the dashboard (campaign → Integrations). To add leads from your own code, use POST /v1/campaigns/{id}/enrollments or /contacts/import.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#getCampaign"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The campaign.","content":{"application/json":{"example":{"success":true,"data":{"id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","name":"Summer Sale Campaign","description":"Outreach for summer promotion","type":"outbound","status":"draft","total_enrollments":0,"active_enrollments":0,"completed_enrollments":0,"goal_reached_count":0,"business_hours":{"enabled":true,"timezone":"America/Los_Angeles","schedule":{"monday":{"enabled":true,"start":"10:00","end":"18:00"},"tuesday":{"enabled":true,"start":"10:00","end":"18:00"},"wednesday":{"enabled":true,"start":"10:00","end":"18:00"}},"holidays":[]},"rate_limits":{"max_concurrent_calls":5,"max_calls_per_hour":50,"max_calls_per_lead_per_day":3,"min_minutes_between_attempts":30},"created_at":"2026-10-02T02:26:06.132516+00:00","updated_at":"2026-10-02T02:26:06.132516+00:00","webhook_url":"https://bjhfvncvsmipuuijvkno.functions.supabase.co/campaign-webhook/f8d1c003-b292-448d-b8d8-1dd521b94b3b","outbound_channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","default_contact_list_id":"d2406224-509f-4177-9a5a-b50bee1e7590","api_import_enabled":false,"deduplication":"skip","callbacks_enabled":false,"max_call_seconds":null,"retries":{"enabled":true,"mode":"simple","max_retries":2,"delay_minutes":60,"exponential_backoff":true},"contact_lists":[{"id":"d2406224-509f-4177-9a5a-b50bee1e7590","name":"Leads de octubre","description":null,"contact_count":240,"is_default":true,"linked_at":"2026-10-23T19:12:44.120000+00:00"}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateCampaign","summary":"Update a campaign","description":"Changes the fields you send. `status` runs the lifecycle steps instead of a bare write: `paused` stops an active campaign like POST /pause, `archived` archives a campaign that is not active like DELETE, and the current status changes nothing (so you can send back what GET returned); activate with POST /start.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA campaign created with `outbound_channel_id` keeps that channel on its call step: changing `outbound_channel_id` later changes the campaign default, not that step. PUT /v1/campaigns/{id}/workflow with a `preset` moves the calls to its `channel_id`.\n\n`rate_limits.min_minutes_between_attempts` is stored and returned, and it is not applied: the campaign engine never reads it. How long a campaign waits before it calls the same contact again is set by its steps and its retry settings (`retries`). The cap that limits calls to one contact is `max_calls_per_lead_per_day`.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#updateCampaign"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"New name.","type":"string","minLength":1,"maxLength":200},"description":{"description":"New description; an empty string clears it.","type":"string","maxLength":1000},"status":{"description":"`paused` (from active) or `archived` (not while active). `active`, `draft` and `completed` answer 400.","type":"string","enum":["draft","active","paused","completed","archived"]},"outbound_channel_id":{"description":"Another of your voice channels.","type":"string","format":"uuid"},"business_hours":{"description":"New calling hours (replaces the stored ones).","type":"object","properties":{"timezone":{"description":"IANA timezone the hours are in, e.g. America/Mexico_City.","type":"string"},"schedule":{"description":"Open days keyed by weekday (monday, tuesday, wednesday, thursday, friday, saturday, sunday), each {start, end}. `days` is accepted as an older name.","type":"object","properties":{"monday":{"description":"Monday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"tuesday":{"description":"Tuesday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"wednesday":{"description":"Wednesday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"thursday":{"description":"Thursday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"friday":{"description":"Friday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"saturday":{"description":"Saturday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]},"sunday":{"description":"Sunday's window.","type":"object","properties":{"start":{"description":"Opening time, HH:MM (24-hour).","type":"string"},"end":{"description":"Closing time, HH:MM. An end before the start crosses midnight.","type":"string"},"enabled":{"description":"false keeps the window but closes the day.","type":"boolean","default":true}},"required":["start","end"]}}},"holidays":{"description":"Dates (YYYY-MM-DD, in the campaign timezone) the campaign does not call.","type":"array","items":{"description":"A date.","type":"string"}},"enabled":{"description":"false keeps the schedule but lets the campaign call at any hour.","type":"boolean","default":true}},"required":["timezone"]},"rate_limits":{"description":"Caps. What you send replaces the campaign's speed caps (simultaneous, hourly and daily calls and messages): send every one you want to keep, since one left out is no longer applied. The cap on calls to the same contact per day is never dropped that way: it stays as stored unless you send `max_calls_per_lead_per_day`. `null` removes the speed caps and keeps that one. A stored `min_minutes_between_attempts` is kept too, and is not applied (see the notes). Checked against your plan.","type":["object","null"],"properties":{"max_concurrent_calls":{"description":"Simultaneous calls.","type":"integer","minimum":1,"maximum":100},"max_calls_per_hour":{"description":"Calls per hour.","type":"integer","minimum":0},"max_calls_per_day":{"description":"Calls per day.","type":"integer","minimum":0},"max_calls_per_lead_per_day":{"description":"Calls to the same contact per day.","type":"integer","minimum":0},"max_messages_per_hour":{"description":"WhatsApp messages per hour.","type":"integer","minimum":0},"max_messages_per_day":{"description":"WhatsApp messages per day.","type":"integer","minimum":0},"max_concurrent_messages":{"description":"Simultaneous WhatsApp sends.","type":"integer","minimum":1}}},"api_import_enabled":{"description":"true: contacts you enroll or import while the campaign is a draft or paused wait (`status: enrolled`) and are called once it starts, so you can load them before the start; false (the default): those requests answer 409 until the campaign is active. The dashboard calls it API import.","type":"boolean"},"deduplication":{"description":"What enrolling someone already in the campaign does: `skip` (the default) leaves them as they are; `re_enroll` starts them over at the first step; `update_only` updates their details and keeps their progress.","type":"string","enum":["skip","re_enroll","update_only"]},"retries":{"description":"Calling again a contact who didn't answer, got voicemail, was busy or whose call failed (Settings → Retries, simple mode). Sent, it replaces the retry settings: a field you leave out keeps its stored value, and retries set in the dashboard's advanced mode become simple ones. Omitted on create: 2 retries (3 calls in all), the first an hour after the call and the next twice as late, as a campaign made in the dashboard starts.","type":"object","properties":{"enabled":{"description":"false: each contact gets one call.","type":"boolean"},"max_retries":{"description":"Calls after the first one.","type":"integer","minimum":1,"maximum":10},"delay_minutes":{"description":"Minutes before the first retry: one of 5, 15, 30, 60, 120, 240, 1440.","type":"integer","minimum":5,"maximum":1440},"exponential_backoff":{"description":"true: each next retry waits twice as long as the one before.","type":"boolean"}},"required":["enabled"]},"callbacks_enabled":{"description":"true: when someone says \"call me later\", the agent asks when, books it, and the campaign calls back at that time (pending retries wait for the callback). false (the default): such requests are not scheduled.","type":"boolean"},"max_call_seconds":{"description":"The longest one call may last, in whole minutes written as seconds (60 to 7200, a multiple of 60): the carrier ends the call there. null (the default): no limit. Calls from an AiBox line or Zoom Phone don't take it.","type":["integer","null"],"minimum":60,"maximum":7200}}},"example":{"rate_limits":{"max_concurrent_calls":3,"max_calls_per_hour":40,"max_calls_per_lead_per_day":2}}}}},"responses":{"200":{"description":"The updated campaign.","content":{"application/json":{"example":{"success":true,"data":{"id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","name":"Summer Sale Campaign","description":"Outreach for summer promotion","type":"outbound","status":"draft","total_enrollments":0,"active_enrollments":0,"completed_enrollments":0,"goal_reached_count":0,"business_hours":{"enabled":true,"timezone":"America/Los_Angeles","schedule":{"monday":{"enabled":true,"start":"10:00","end":"18:00"},"tuesday":{"enabled":true,"start":"10:00","end":"18:00"},"wednesday":{"enabled":true,"start":"10:00","end":"18:00"}},"holidays":[]},"rate_limits":{"max_calls_per_lead_per_day":2,"min_minutes_between_attempts":30,"max_concurrent_calls":3,"max_calls_per_hour":40},"created_at":"2026-10-02T02:26:06.132516+00:00","updated_at":"2026-10-02T02:28:30.168271+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateCampaign"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent. `validation_error`: A status the campaign cannot move to (activate with POST /start; pause only an active campaign; archive only one that is not active), a channel that is not yours, invalid `business_hours`, or `retries` turned on with no `max_retries` from 1 to 10. `validation_error`: `rate_limits` exceed your plan (`details.validation_errors`, `details.tenant_limits`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The status changed between this request's read and its write (a pause, start or archive landed first). Read the campaign again. `validation_error`: Other requests kept changing the campaign's `rate_limits` while this one tried to write them. An overlapping change is merged for you (the cap on calls per contact per day is taken as it is when the write happens); this answer comes only after several tries. Read the campaign again and retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteCampaign","summary":"Archive a campaign","description":"Archives a campaign that is not active (pause it first). An archived campaign stays readable and appears in the list; it cannot start again.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#deleteCampaign"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The campaign is archived.","content":{"application/json":{"example":{"success":true,"data":{"deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteCampaign"}}}}}},"400":{"description":"`validation_error`: The campaign is active: pause it first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/workflow":{"get":{"operationId":"getCampaignWorkflow","summary":"Get a campaign's steps","description":"Returns the campaign (`id`, `name`, `status`) with its workflow: its steps (`nodes`, each with `data.type` and the step's settings) and how they connect (`edges`; a condition's Yes branch has `sourceHandle: \"true\"` and its No branch `\"false\"`), and the `revision` a save of steps built from them names in `expected_revision`. Webhook steps' header values and URL credentials come masked.\n\nRequires the `campaigns:read` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#getCampaignWorkflow"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The steps and their revision.","content":{"application/json":{"example":{"success":true,"data":{"id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","name":"Citas de octubre","status":"draft","workflow":{"nodes":[{"id":"start","type":"trigger","position":{"x":300,"y":0},"data":{"type":"trigger","trigger_type":"manual","label":"Start"}},{"id":"call","type":"action","position":{"x":300,"y":130},"data":{"type":"action","action_type":"voice_call","label":"Booking call","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64"}},{"id":"answered","type":"condition","position":{"x":300,"y":260},"data":{"type":"condition","condition_type":"call_outcome","label":"Answered?","outcomes":["answered"]}},{"id":"analyze","type":"action","position":{"x":120,"y":390},"data":{"type":"action","action_type":"analyze_conversation","label":"Read the call","analysis_source":"auto"}},{"id":"decision","type":"condition","position":{"x":120,"y":520},"data":{"type":"condition","condition_type":"analysis_result","label":"Appointment booked?","analysis_node_id":"analyze","analysis_field":"appointment_booked","analysis_operator":"is_true"}},{"id":"end-yes","type":"terminal","position":{"x":0,"y":650},"data":{"type":"terminal","terminal_type":"success","label":"Appointment booked","lead_response":"appointment_booked"}},{"id":"end-no","type":"terminal","position":{"x":240,"y":650},"data":{"type":"terminal","terminal_type":"failure","label":"Not booked","lead_response":"not_interested"}},{"id":"end-not-reached","type":"terminal","position":{"x":500,"y":390},"data":{"type":"terminal","terminal_type":"failure","label":"Didn't answer","lead_response":"unknown"}}],"edges":[{"id":"e-1","source":"start","target":"call"},{"id":"e-2","source":"call","target":"answered"},{"id":"e-3","source":"answered","target":"analyze","sourceHandle":"true"},{"id":"e-4","source":"decision","target":"end-yes","sourceHandle":"true"},{"id":"e-5","source":"decision","target":"end-no","sourceHandle":"false"},{"id":"e-6","source":"analyze","target":"decision"},{"id":"e-7","source":"answered","target":"end-not-reached","sourceHandle":"false"}]},"revision":3}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"setCampaignWorkflow","summary":"Replace a campaign's steps","description":"Replaces the campaign's steps, with the checks of the dashboard's own save: the workflow rules, every number and agent a step names being yours, WhatsApp steps on an Official WhatsApp number. Send `preset` for the steps the dashboard builds for a purpose (call from `channel_id`, check whether the call was answered, the AI reads the call, success by the purpose's question), or `workflow` for any steps the dashboard's builder makes (several numbers, waits, WhatsApp templates, CRM steps, switching agents). On an active campaign the new steps apply to each contact's next step.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA call step has no opening or goal of its own: each call speaks with its agent's instructions and goals. Sent ones are dropped, as in the dashboard.\n\nWebhook steps' header values and URL credentials (a sign-in, or a query value such as `token`) read masked (`••••••`, `redacted@`). Sent back as read they keep their stored values, as long as the step's `url` calls the same address: the same url apart from its credentials. A step pointed at another address keeps none of them: send its real header values and URL credentials (a header set to null is removed).","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#setCampaignWorkflow"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"preset":{"description":"The dashboard's steps for a purpose. Send this or `workflow`.","type":"object","properties":{"purpose":{"description":"What counts as success: the AI reads each answered call and the campaign ends it as a success when the answer to the purpose's question is yes. `general` (Did the person show clear interest in continuing?); `lead_qualification` (Did the person show clear interest in continuing?); `appointment_booking` (Was an appointment booked with a confirmed date and time?); `appointment_confirmation` (Did the person confirm the appointment?); `payment_reminder` (Did the person commit to paying (with a date if given)?); `reactivation` (Did the person show clear interest in continuing?); `customer_survey` (Did the person answer the survey questions?). `general` is what the dashboard builds.","type":"string","enum":["general","lead_qualification","appointment_booking","appointment_confirmation","payment_reminder","reactivation","customer_survey"]},"agent_id":{"description":"The agent that makes the calls, named on the call step: one of yours, not archived, with a deployed version. Without it the agent deployed on `channel_id` talks, as in the dashboard.","type":"string","format":"uuid"},"step_names_language":{"description":"The language the steps are named in on the dashboard.","type":"string","enum":["en","es"],"default":"en"},"channel_id":{"description":"The voice channel the calls go out from; the campaign's `outbound_channel_id` when left out. It also becomes the campaign's `outbound_channel_id`.","type":"string","format":"uuid"}}},"workflow":{"description":"A whole workflow, as GET returns it. A step without `position` is placed under the one before. Send this or `preset`.","type":"object","properties":{"nodes":{"description":"The steps: `id`, `type` (trigger, action, condition or terminal), `data` and an optional `position`.","type":"array","minItems":1,"maxItems":200,"items":{"description":"A step.","type":"object","additionalProperties":true}},"edges":{"description":"The connections: `id`, `source`, `target` and, from a condition, `sourceHandle` (`\"true\"` or `\"false\"`).","type":"array","maxItems":400,"items":{"description":"A connection.","type":"object","additionalProperties":true}}},"required":["nodes","edges"]},"expected_revision":{"description":"The `revision` GET answered: the save is refused (409) when the steps changed since. Left out, the steps are replaced whatever changed.","type":"integer","minimum":1}}},"example":{"preset":{"purpose":"appointment_booking","agent_id":"550e8400-e29b-41d4-a716-446655440000","step_names_language":"es"},"expected_revision":3}}}},"responses":{"200":{"description":"Saved: the steps as stored, their new `revision`, the campaign's `outbound_channel_id` and the rules' non-blocking `warnings`.","content":{"application/json":{"example":{"success":true,"data":{"workflow":{"nodes":[{"id":"start","type":"trigger","position":{"x":300,"y":0},"data":{"type":"trigger","trigger_type":"manual","label":"Start"}},{"id":"call","type":"action","position":{"x":300,"y":130},"data":{"type":"action","action_type":"voice_call","label":"Booking call","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64"}},{"id":"answered","type":"condition","position":{"x":300,"y":260},"data":{"type":"condition","condition_type":"call_outcome","label":"Answered?","outcomes":["answered"]}},{"id":"analyze","type":"action","position":{"x":120,"y":390},"data":{"type":"action","action_type":"analyze_conversation","label":"Read the call","analysis_source":"auto"}},{"id":"decision","type":"condition","position":{"x":120,"y":520},"data":{"type":"condition","condition_type":"analysis_result","label":"Appointment booked?","analysis_node_id":"analyze","analysis_field":"appointment_booked","analysis_operator":"is_true"}},{"id":"end-yes","type":"terminal","position":{"x":0,"y":650},"data":{"type":"terminal","terminal_type":"success","label":"Appointment booked","lead_response":"appointment_booked"}},{"id":"end-no","type":"terminal","position":{"x":240,"y":650},"data":{"type":"terminal","terminal_type":"failure","label":"Not booked","lead_response":"not_interested"}},{"id":"end-not-reached","type":"terminal","position":{"x":500,"y":390},"data":{"type":"terminal","terminal_type":"failure","label":"Didn't answer","lead_response":"unknown"}}],"edges":[{"id":"e-1","source":"start","target":"call"},{"id":"e-2","source":"call","target":"answered"},{"id":"e-3","source":"answered","target":"analyze","sourceHandle":"true"},{"id":"e-4","source":"decision","target":"end-yes","sourceHandle":"true"},{"id":"e-5","source":"decision","target":"end-no","sourceHandle":"false"},{"id":"e-6","source":"analyze","target":"decision"},{"id":"e-7","source":"answered","target":"end-not-reached","sourceHandle":"false"}]},"revision":4,"outbound_channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","warnings":[]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setCampaignWorkflow"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The steps break a workflow rule (`details.validation_errors`), name a number or agent that isn't yours, or send WhatsApp messages from a number that isn't an Official WhatsApp one; a preset's number isn't one of your voice channels, or its agent is archived or has no deployed version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`workflow_changed`: The steps changed since `expected_revision` (`details.revision` is the current one): read them again and rebuild. `steps_in_use`: Contacts are still on steps this change removes (`details.steps`: each step's `node_id`, `label` and `contacts`). Keep those steps' ids, or wait until the contacts move on.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/start":{"post":{"operationId":"startCampaign","summary":"Start a campaign","description":"Activates a draft or paused campaign: contacts waiting in it are queued and dialing begins within the calling hours. Runs the dashboard's start checks first: an outbound campaign needs an outbound channel of your workspace, its `rate_limits` must fit your plan, its workflow must pass the workflow rules, and what the steps use must be ready (each call step's number has a deployed agent, WhatsApp steps use a Meta WhatsApp number, CRM steps have their connection).\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe checks are the dashboard's own, so a campaign that starts there starts here. Fix what `details.issues` names (put the agent on the number with `PUT /v1/channels/{id}/agent`, deploy it, or connect the CRM in the dashboard), then start again.","tags":["Campaigns"],"x-required-scope":"campaigns:write","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#startCampaign"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The campaign is active.","content":{"application/json":{"example":{"success":true,"data":{"id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","name":"Summer Sale Campaign","status":"active","type":"outbound","updated_at":"2026-10-02T02:28:30.318485+00:00","enrollments_activated":12},"message":"Campaign started successfully"}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"startCampaign"}}}}}},"400":{"description":"`validation_error`: The campaign is already active, completed or archived; an outbound campaign has no outbound channel; or the channel is not one of yours. `validation_error`: `rate_limits` exceed your plan (`details.validation_errors`, `details.tenant_limits`). `validation_error`: The campaign is not ready to start: `details.issues` lists each problem as `{code, message}` (for example `number_no_agent`, `call_agent_unpublished`, `whatsapp_number_not_meta`, `hubspot_not_connected`, or `workflow_rule` for a rule of the workflow itself), and `details.validation_errors` repeats the messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/pause":{"post":{"operationId":"pauseCampaign","summary":"Pause a campaign","description":"Pauses an active campaign: queued steps are cancelled, runnable contacts are set aside and calls that have not been dialed are cancelled. A call already in progress finishes. Start it again with POST /start.\n\nRequires the `campaigns:write` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:write","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#pauseCampaign"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The campaign is paused.","content":{"application/json":{"example":{"success":true,"data":{"id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","name":"Summer Sale Campaign","status":"paused","type":"outbound","updated_at":"2026-10-02T03:00:00.000000+00:00","cancel_event_sent":true,"enrollments_deactivated":9,"voice_jobs_cancelled":2},"message":"Campaign paused successfully"}}}},"400":{"description":"`validation_error`: The campaign is not active (or already paused).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/enrollments":{"get":{"operationId":"listCampaignEnrollments","summary":"List a campaign's enrollments","description":"Lists the contacts enrolled in a campaign, newest first, with their status, lead response and last call outcome. `enrolled` means waiting for the campaign to start. `cooldown_until` is set when the workspace's \"don't call again for X days\" rule holds the number: it is called after that moment, or, when the workspace leaves such numbers out, the enrollment is `removed`.\n\nRequires the `campaigns:read` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#listCampaignEnrollments"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only enrollments in this status.","schema":{"type":"string","enum":["enrolled","queued","in_progress","retry_scheduled","callback_scheduled","paused","completed","failed","exhausted","rejected","removed"]}},{"name":"lead_response","in":"query","required":false,"description":"Only enrollments with this lead response.","schema":{"type":"string","enum":["unknown","interested","not_interested","callback_requested","appointment_booked","do_not_contact"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of enrollments.","content":{"application/json":{"example":{"success":true,"data":[{"id":"7593112e-56ab-4a08-858f-21035eb1bacc","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+525512345678","status":"completed","lead_response":"interested","final_call_outcome":"answered","failure_reason":null,"failure_detail":null,"current_retry_attempt":0,"stage_id":null,"source":"api","source_metadata":{"api_key_id":"7b7a0ce4-0000-4000-8000-000000000001","received_at":"2026-10-02T02:28:30.401Z"},"lead_data":{"name":"Ana López","product":"Plan Pro"},"enrolled_at":"2026-10-02T02:28:30.521857+00:00","next_action_at":null,"cooldown_until":null,"completed_at":"2026-10-02T02:41:12.000000+00:00","last_activity_at":"2026-10-02T02:41:12.000000+00:00"}],"pagination":{"total":1,"limit":50,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"enrollCampaignContact","summary":"Enroll one contact","description":"Finds the contact by phone number (or creates it) and enrolls it. On an active campaign the first call happens within the calling hours, usually within seconds. With API import enabled on the campaign, a draft or paused campaign holds the contact (`status: enrolled`) until it starts.\n\nRequires the `campaigns:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#enrollCampaignContact"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_number":{"description":"Number with the country code, e.g. +5215512345678 (E.164 recommended).","type":"string","minLength":5,"maxLength":30},"name":{"description":"Contact name.","type":"string","maxLength":200},"email":{"description":"Contact email.","type":"string","format":"email","maxLength":320},"lead_data":{"description":"Any fields the agent should know about this lead (product, last purchase, …).","type":"object","additionalProperties":true},"source_metadata":{"description":"Where the lead came from; stored with the enrollment.","type":"object","additionalProperties":true}},"required":["phone_number"]},"example":{"phone_number":"+5215512345678","name":"Ana López","lead_data":{"product":"Plan Pro"}}}}},"responses":{"201":{"description":"Enrolled. `status` is `queued` (will be called) or `enrolled` (held until the campaign starts); `action` is `created`, or `updated` when the campaign's deduplication policy updated an existing enrollment, whose current `status` is answered. When someone talked with this number recently and the workspace's \"don't call again for X days\" rule is on, `cooldown_until` says until when it waits (`queued`), or `status` is `removed` when the workspace leaves such numbers out.","content":{"application/json":{"example":{"success":true,"data":{"enrollment_id":"7593112e-56ab-4a08-858f-21035eb1bacc","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","action":"created","status":"queued","cooldown_until":null,"campaign_status":"active"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `phone_number` is not a valid number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: Already enrolled (`details.enrollment_id`), the contact opted out (`details.reason: do_not_contact`), or the campaign is not active and API import is off (`details.campaign_status`, `details.api_import_enabled`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/enrollments/{enrollment_id}":{"get":{"operationId":"getCampaignEnrollment","summary":"Get an enrollment","description":"Returns one contact's place in a campaign, as the dashboard's enrollment drawer shows it: status, lead response and stage, the step it is on (`current_step`), its attempts, when its next call is and why (`next_call`), and its 10 latest calls (`recent_calls`; each call's transcript is on GET /v1/calls/{id}).\n\nRequires the `campaigns:read` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#getCampaignEnrollment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"enrollment_id","in":"path","required":true,"description":"The enrollment id (`id` in the campaign's enrollments list).","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The enrollment. `next_call.kind` is `in_progress` (on a call now), `retry`, `callback` or `queued`, with `at` (when; null while on a call) and `reason` (the retry's or the callback's); `next_call` is null when no call is pending.","content":{"application/json":{"example":{"success":true,"data":{"id":"7593112e-56ab-4a08-858f-21035eb1bacc","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+525512345678","status":"retry_scheduled","lead_response":"unknown","final_call_outcome":"no_answer","stage_id":null,"current_node_id":"call-1","total_call_attempts":1,"max_total_attempts":3,"current_retry_attempt":1,"max_retry_attempts":2,"scheduled_retry_at":"2026-10-02T16:00:00+00:00","retry_reason":"no_answer","scheduled_callback_at":null,"callback_reason":null,"next_action_at":"2026-10-02T16:00:00+00:00","cooldown_until":null,"source":"api","source_metadata":{"api_key_id":"7b7a0ce4-0000-4000-8000-000000000001","received_at":"2026-10-02T14:58:30.401Z"},"lead_data":{"name":"Ana López","product":"Plan Pro"},"enrolled_at":"2026-10-02T14:58:30.521857+00:00","last_activity_at":"2026-10-02T15:01:12.000000+00:00","completed_at":null,"display_name":"Ana López","stage_name":null,"current_step":{"id":"call-1","label":"Call lead"},"next_call":{"kind":"retry","at":"2026-10-02T16:00:00+00:00","reason":"no_answer"},"failure_reason":null,"failure_detail":null,"recent_calls":[{"id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","status":"no_answer","started_at":"2026-10-02T15:00:02.000000+00:00","duration_seconds":0,"call_outcome":"no_answer","failure_reason":"no_answer","failure_detail":"It rang 30 s and nobody picked up."}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign or enrollment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateCampaignEnrollment","summary":"Set an enrollment's lead response or stage","description":"Sets the contact's lead response, its stage, or both, as a person does in the enrollment drawer. It places no call and stops none: call the contact again with POST /requeue, stop calling them with POST /remove. `do_not_contact` opts the person out of every campaign: their other waiting enrollments close, and nothing calls or messages them until the opt-out is lifted (PATCH /v1/contacts/{id} with `do_not_contact: false`).\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#updateCampaignEnrollment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"enrollment_id","in":"path","required":true,"description":"The enrollment id (`id` in the campaign's enrollments list).","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_response":{"description":"The contact's answer.","type":"string","enum":["unknown","interested","not_interested","callback_requested","appointment_booked","do_not_contact"]},"stage_id":{"description":"One of the campaign's stages (its pipeline in the dashboard); null takes the contact out of its stage.","type":["string","null"],"format":"uuid"}}},"example":{"lead_response":"interested"}}}},"responses":{"200":{"description":"The enrollment, as GET answers it.","content":{"application/json":{"example":{"success":true,"data":{"id":"7593112e-56ab-4a08-858f-21035eb1bacc","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+525512345678","status":"retry_scheduled","lead_response":"interested","final_call_outcome":"no_answer","stage_id":null,"current_node_id":"call-1","total_call_attempts":1,"max_total_attempts":3,"current_retry_attempt":1,"max_retry_attempts":2,"scheduled_retry_at":"2026-10-02T16:00:00+00:00","retry_reason":"no_answer","scheduled_callback_at":null,"callback_reason":null,"next_action_at":"2026-10-02T16:00:00+00:00","cooldown_until":null,"source":"api","source_metadata":{"api_key_id":"7b7a0ce4-0000-4000-8000-000000000001","received_at":"2026-10-02T14:58:30.401Z"},"lead_data":{"name":"Ana López","product":"Plan Pro"},"enrolled_at":"2026-10-02T14:58:30.521857+00:00","last_activity_at":"2026-10-02T15:01:12.000000+00:00","completed_at":null,"display_name":"Ana López","stage_name":null,"current_step":{"id":"call-1","label":"Call lead"},"next_call":{"kind":"retry","at":"2026-10-02T16:00:00+00:00","reason":"no_answer"},"failure_reason":null,"failure_detail":null,"recent_calls":[{"id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","status":"no_answer","started_at":"2026-10-02T15:00:02.000000+00:00","duration_seconds":0,"call_outcome":"no_answer","failure_reason":"no_answer","failure_detail":"It rang 30 s and nobody picked up."}]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateCampaignEnrollment"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: Neither field is sent, or `stage_id` is not one of this campaign's stages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign or enrollment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/enrollments/{enrollment_id}/requeue":{"post":{"operationId":"requeueCampaignEnrollment","summary":"Call a contact again","description":"Starts the contact over from the campaign's first step, as Call again does in the enrollment drawer: status `queued`, its retries, callbacks and last outcome cleared, its calls for the day counted from zero. An active campaign calls it within the calling hours; a draft or paused one keeps it queued until it starts.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nIt calls a real person and spends credits: confirm with the user first.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#requeueCampaignEnrollment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"enrollment_id","in":"path","required":true,"description":"The enrollment id (`id` in the campaign's enrollments list).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Queued. The enrollment, as GET answers it.","content":{"application/json":{"example":{"success":true,"data":{"id":"7593112e-56ab-4a08-858f-21035eb1bacc","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+525512345678","status":"queued","lead_response":"unknown","final_call_outcome":null,"stage_id":null,"current_node_id":null,"total_call_attempts":1,"max_total_attempts":3,"current_retry_attempt":0,"max_retry_attempts":2,"scheduled_retry_at":null,"retry_reason":null,"scheduled_callback_at":null,"callback_reason":null,"next_action_at":"2026-10-02T15:10:00.000000+00:00","cooldown_until":null,"source":"api","source_metadata":{"api_key_id":"7b7a0ce4-0000-4000-8000-000000000001","received_at":"2026-10-02T14:58:30.401Z"},"lead_data":{"name":"Ana López","product":"Plan Pro"},"enrolled_at":"2026-10-02T14:58:30.521857+00:00","last_activity_at":"2026-10-02T15:01:12.000000+00:00","completed_at":null,"display_name":"Ana López","stage_name":null,"current_step":null,"next_call":{"kind":"queued","at":"2026-10-02T15:10:00.000000+00:00","reason":null},"failure_reason":null,"failure_detail":null,"recent_calls":[{"id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","status":"no_answer","started_at":"2026-10-02T15:00:02.000000+00:00","duration_seconds":0,"call_outcome":"no_answer","failure_reason":"no_answer","failure_detail":"It rang 30 s and nobody picked up."}]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"requeueCampaignEnrollment"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign or enrollment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The contact is on a call right now (`details.reason` `CONTACT_ON_CALL`): try again when the call ends. `conflict`: The person asked not to be contacted (`details.reason` `do_not_contact`). Lift the opt-out first, only when they asked for it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`campaign_not_ready`: The campaign can't call anyone: `details.blocking_conditions` lists each problem as `{code, message, action}` (`RATE_LIMIT_ZERO`: `rate_limits.max_calls_per_day` is 0; `NO_OUTBOUND_CHANNEL`: no `outbound_channel_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/enrollments/{enrollment_id}/remove":{"post":{"operationId":"removeCampaignEnrollment","summary":"Take a contact out of a campaign","description":"Stops the campaign calling or messaging the contact, as Remove does in the enrollment drawer: status `removed`, its pending callbacks cancelled. Its calls and results stay, and the contact stays in your contacts. Removing a removed contact changes nothing. To stop every campaign, set `lead_response` to `do_not_contact` instead.\n\nRequires the `campaigns:write` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#removeCampaignEnrollment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"enrollment_id","in":"path","required":true,"description":"The enrollment id (`id` in the campaign's enrollments list).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Removed. The enrollment, as GET answers it.","content":{"application/json":{"example":{"success":true,"data":{"id":"7593112e-56ab-4a08-858f-21035eb1bacc","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","phone_number":"+525512345678","status":"removed","lead_response":"unknown","final_call_outcome":"no_answer","stage_id":null,"current_node_id":"call-1","total_call_attempts":1,"max_total_attempts":3,"current_retry_attempt":1,"max_retry_attempts":2,"scheduled_retry_at":"2026-10-02T16:00:00+00:00","retry_reason":"no_answer","scheduled_callback_at":null,"callback_reason":null,"next_action_at":"2026-10-02T16:00:00+00:00","cooldown_until":null,"source":"api","source_metadata":{"api_key_id":"7b7a0ce4-0000-4000-8000-000000000001","received_at":"2026-10-02T14:58:30.401Z"},"lead_data":{"name":"Ana López","product":"Plan Pro"},"enrolled_at":"2026-10-02T14:58:30.521857+00:00","last_activity_at":"2026-10-02T15:01:12.000000+00:00","completed_at":"2026-10-02T15:20:00.000000+00:00","display_name":"Ana López","stage_name":null,"current_step":{"id":"call-1","label":"Call lead"},"next_call":null,"failure_reason":null,"failure_detail":null,"recent_calls":[{"id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","status":"no_answer","started_at":"2026-10-02T15:00:02.000000+00:00","duration_seconds":0,"call_outcome":"no_answer","failure_reason":"no_answer","failure_detail":"It rang 30 s and nobody picked up."}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign or enrollment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The contact is on a call right now (`details.reason` `CONTACT_ON_CALL`): try again when the call ends.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/contacts/import":{"post":{"operationId":"importCampaignContacts","summary":"Import contacts in bulk","description":"Enrolls up to 500 contacts in one request and reports what happened to each row. Each row is validated (phone, duplicates in the request, already enrolled, opted out); the run is recorded in the campaign's import history.\n\nRequires the `campaigns:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nImported contacts are also added to the campaign's default contact list, if it has one. When another campaign also uses that list, they go to a new list for this campaign alone instead, `{campaign name} · contacts`, which becomes its default list: contacts added to a list are enrolled in every draft, paused or active outbound campaign that uses it.\n\nFor a large file (up to 4 GiB, millions of rows), use POST /v1/contact-imports: the file goes straight to storage and is imported in the background, with progress to follow.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#importCampaignContacts"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contacts":{"description":"Rows. Any other key with a text or number value is kept in the enrollment's lead data (custom fields).","type":"array","minItems":1,"maxItems":500,"items":{"description":"A contact row.","type":"object","properties":{"phone_number":{"description":"Number with the country code.","type":"string","maxLength":40},"name":{"description":"Full name (`display_name` also accepted).","type":"string","maxLength":200},"first_name":{"description":"First name.","type":"string","maxLength":100},"last_name":{"description":"Last name.","type":"string","maxLength":100},"email":{"description":"Email; an invalid one is dropped.","type":"string","maxLength":320},"company":{"description":"Company (fills the contact's company when empty).","type":"string","maxLength":200},"external_id":{"description":"Your id for the contact, stored on it as `crm_ids[source]`.","type":"string","maxLength":200},"notes":{"description":"Notes for the agent.","type":"string","maxLength":2000},"lead_data":{"description":"Extra fields for the agent.","type":"object","additionalProperties":true},"fields":{"description":"The contact's field values by key (`{ \"interes\": \"Casa\" }`, see /contact-fields), checked against each field's type before anything is written. Only existing fields; null or an empty text leaves a value as it is.","type":"object","additionalProperties":true}},"required":["phone_number"]}},"source":{"description":"Origin label (lowercase letters, digits, - and _, up to 40), e.g. salesforce. Stored as the enrollments' source and the key of `external_id`. Platform labels (contact_list, csv, manual, webhook, campaign, copilot, hubspot*, kommo*, facebook_ads, google_ads, meta_lead_ads) are refused.","type":"string","default":"api"}},"required":["contacts"]},"example":{"source":"my_crm","contacts":[{"phone_number":"+5215512345678","name":"Ana López","company":"Acme","external_id":"crm-1001"},{"phone_number":"+14155550123","name":"Bob"},{"phone_number":"12","name":"Invalid"}]}}}},"responses":{"200":{"description":"Per-row results. Row `status`: `enrolled` (queued to call), `held` (waits for the start), `updated` (deduplication policy update_only, or re_enroll for a person being called or with a callback booked), `skipped` (`reason: already_enrolled`), `rejected` (`reason`: missing_phone, invalid_phone, duplicate_in_request, do_not_contact, campaign_not_accepting, contact_error, enrollment_error).","content":{"application/json":{"example":{"success":true,"data":{"batch_id":"25ea2e90-8f89-4684-afc4-e1cef81ce0bc","campaign_status":"active","summary":{"total":3,"enrolled":2,"held":0,"updated":0,"skipped":0,"rejected":1},"rows":[{"index":0,"phone_number":"+5215512345678","status":"enrolled","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","enrollment_id":"7593112e-56ab-4a08-858f-21035eb1bacc"},{"index":1,"phone_number":"+14155550123","status":"enrolled","contact_id":"eaa5bc4d-b54a-4cb2-bcf8-c1b523f716eb","enrollment_id":"f47c40af-a57c-48f0-9dfb-03cb51ce8cf0"},{"index":2,"phone_number":"12","status":"rejected","reason":"invalid_phone","message":"Phone number must have 8 to 15 digits including the country code"}]}}}}},"400":{"description":"`validation_error`: The body is invalid; a row that breaks the schema (no phone_number, a field too long) fails the whole request, without a row index.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The campaign is not active and API import is off, or it is completed or archived (`details.campaign_status`, `details.api_import_enabled`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error`: A row's `fields` names an unknown or archived field, or a value doesn't fit its field (`details.reason: contact_field_values_invalid`, `details.issues` with each `row`, `field` and `code`). Nothing is imported.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/contact-lists/{list_id}":{"put":{"operationId":"linkCampaignContactList","summary":"Link a contact list","description":"Makes a contact list's members the campaign's contacts, as the dashboard's Contacts step does (GET /v1/campaigns/{id} lists the linked ones). In an outbound campaign that is a draft or paused they wait for the start (`members: held`); in an active one they are queued and called within the calling hours (`members: queued`). Members added to the list later join too. The first list linked becomes the campaign's default list. Linking a list that is linked already changes nothing.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nNeeds `contact_lists:read` as well as `campaigns:write`. Unlinking needs `campaigns:write` only.\n\nThe first 1,000 members are enrolled at once; the rest of a bigger list in the background, within minutes. Contacts who opted out or are already in the campaign are skipped.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#linkCampaignContactList"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"list_id","in":"path","required":true,"description":"The contact list id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Linked. `members` says what happens to the list's members now: `held`, `queued`, or `not_enrolled` (an inbound, completed or archived campaign).","content":{"application/json":{"example":{"success":true,"data":{"campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","list_id":"d2406224-509f-4177-9a5a-b50bee1e7590","already_linked":false,"is_default":true,"campaign_status":"draft","members":"held"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"linkCampaignContactList"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key lacks `contact_lists:read` (`details.required_scope`): linking makes the list's members the campaign's enrollments, whose phone numbers `campaigns:read` reads.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No campaign or contact list with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"unlinkCampaignContactList","summary":"Unlink a contact list","description":"Stops a contact list from feeding the campaign: its members' enrollments that are still waiting (enrolled, queued, scheduled for a retry or a callback, paused) are removed; the ones already called keep their results. A big list's are removed in the background, within minutes. The campaign's default list is cleared when it was this one.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#unlinkCampaignContactList"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"list_id","in":"path","required":true,"description":"The contact list id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Unlinked.","content":{"application/json":{"example":{"success":true,"data":{"campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","list_id":"d2406224-509f-4177-9a5a-b50bee1e7590","unlinked":true,"default_contact_list_id":null}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"unlinkCampaignContactList"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same). `not_found`: The list is not linked to the campaign.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/scheduled-calls":{"get":{"operationId":"listScheduledCalls","summary":"List scheduled calls","description":"Lists the workspace's coming campaign calls, as the dashboard's Scheduled calls page does: contacts queued, waiting for a retry or a callback, paused with a next step, or on a call now, the soonest first. Each row is one enrollment (its `id`); `status` is the call's (`queued`, `in_progress`, `retry_scheduled` or `callback_scheduled`), `scheduled_at` when it is due (null while on a call), and `from_channel` the campaign's outbound number.\n\nRequires the `campaigns:read` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#listScheduledCalls"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"`retry` or `callback`: the contacts waiting for one. `queued`, `in_progress` or `paused`: the enrollment's status.","schema":{"type":"string","enum":["queued","retry","callback","in_progress","paused"]}},{"name":"campaign_id","in":"query","required":false,"description":"Only this campaign's calls.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of scheduled calls.","content":{"application/json":{"example":{"success":true,"data":[{"id":"7593112e-56ab-4a08-858f-21035eb1bacc","enrollment_id":"7593112e-56ab-4a08-858f-21035eb1bacc","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","campaign_name":"Summer Sale Campaign","campaign_status":"active","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","contact_name":"Ana López","phone_number":"+525512345678","status":"retry_scheduled","enrollment_status":"retry_scheduled","scheduled_at":"2026-10-02T16:00:00+00:00","reason":"no_answer","attempts":1,"max_attempts":3,"current_retry_attempt":1,"max_retry_attempts":2,"lead_response":"unknown","final_call_outcome":"no_answer","from_channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Sales line","phone_number":"+525598765432"},"enrolled_at":"2026-10-02T14:58:30.521857+00:00","last_activity_at":"2026-10-02T15:01:12.000000+00:00"}],"pagination":{"total":1,"limit":50,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/scheduled-calls/{id}/cancel":{"post":{"operationId":"cancelScheduledCall","summary":"Cancel a scheduled call","description":"Cancels a contact's coming campaign call, as Cancel does on the Scheduled calls page: the enrollment ends as `failed` (`retry_reason` `manual_cancel`) with no retry, callback or next step left. A call in progress is refused: end it with POST /v1/calls/{id}/terminate.\n\nRequires the `campaigns:write` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#cancelScheduledCall"},"parameters":[{"name":"id","in":"path","required":true,"description":"The scheduled call's id (`id` in GET /v1/scheduled-calls: its enrollment's id).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cancelled.","content":{"application/json":{"example":{"success":true,"data":{"id":"7593112e-56ab-4a08-858f-21035eb1bacc","enrollment_id":"7593112e-56ab-4a08-858f-21035eb1bacc","campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","cancelled":true,"enrollment_status":"failed","retry_reason":"manual_cancel"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No scheduled call with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The contact is on a call right now (`details.reason` `CONTACT_ON_CALL`), or has no call to cancel (`NO_PENDING_CALL`, with `details.enrollment_status`): it finished, or waits for its campaign to start (take it out with POST /v1/campaigns/{id}/enrollments/{enrollment_id}/remove).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/variants":{"get":{"operationId":"getCampaignVariants","summary":"Get a campaign's A/B test","description":"The campaign's A/B test as its Settings show it: each variant (A, B, C) with its agent, the version its calls use (pinned at its first call; null before it and once the test is promoted, when calls follow the agent's live version), its voice and its share of the contacts that enter the campaign from now on. `variants` is empty when the campaign has no test. With `calls:read` too, each variant carries its results to date (contacts, calls, answered, interested or booked, answer and conversion rates) and `unassigned_contacts` counts the contacts added before the test; without it both are null, as the dashboard shows results only to those who see every call.\n\nRequires the `campaigns:read` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#getCampaignVariants"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The A/B test.","content":{"application/json":{"example":{"success":true,"data":{"variants":[{"id":"248bc6c9-d8e4-4e98-83d0-8bf9dbab1872","label":"A","position":1,"name":"Warm opener","agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Sofía"},"agent_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"agent_live_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"voice":{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center"},"weight":50,"promoted_at":null,"first_called_at":"2026-10-20T15:02:11.000Z","pinned_at":"2026-10-20T15:02:11.000Z","moved_at":null,"results":{"contacts":412,"attempted":398,"reached":241,"interested":57,"booked":31,"transferred":4,"calls":690,"answered_calls":251,"answer_rate":0.3638,"conversion_rate":0.1383}},{"id":"1f677692-f174-4dab-befc-4568ebc14452","label":"B","position":2,"name":"Direct opener","agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Sofía"},"agent_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"agent_live_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"voice":null,"weight":50,"promoted_at":null,"first_called_at":"2026-10-20T15:02:11.000Z","pinned_at":"2026-10-20T15:02:11.000Z","moved_at":null,"results":{"contacts":405,"attempted":398,"reached":241,"interested":41,"booked":31,"transferred":4,"calls":690,"answered_calls":251,"answer_rate":0.3638,"conversion_rate":0.1012}}],"promoted":false,"unassigned_contacts":128}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: A/B tests aren't available for a few minutes during a release.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"setCampaignVariants","summary":"Save a campaign's A/B test","description":"Saves the whole test, as Save does on the campaign's A/B test: an empty list ends it, or 2 to 3 variants whose shares add up to 100. Send a kept variant's `id`; a variant without one is new. Each variant calls with an active agent that has a deployed version, and optionally a voice from GET /v1/voices; two variants can't share the same agent and voice. A variant that has called keeps its agent and voice and can't be removed. New contacts are split by the shares from now on; contacts already in a variant keep it.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's own rules as the member who created the key: that member needs a role that edits campaigns (owner, admin, builder or operator). A key whose creator has left the workspace, and a reseller key, are refused.\n\nOn an active campaign it changes, at once, which agent and voice call the contacts that enter it: confirm with the user first.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#setCampaignVariants"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"variants":{"description":"The test: [] for none, else 2-3 variants.","type":"array","maxItems":3,"items":{"description":"A variant.","type":"object","properties":{"id":{"description":"A kept variant's id.","type":"string","format":"uuid"},"agent_id":{"description":"The agent the variant calls with.","type":"string","format":"uuid"},"weight":{"description":"Its share of new contacts, in percent.","type":"integer","minimum":0,"maximum":100},"voice_id":{"description":"A voice from GET /v1/voices; null or absent keeps the agent's own.","type":["string","null"]},"name":{"description":"A name to tell it apart (A, B and C are the labels).","type":["string","null"],"maxLength":60}},"required":["agent_id","weight"]}}},"required":["variants"]},"example":{"variants":[{"id":"248bc6c9-d8e4-4e98-83d0-8bf9dbab1872","agent_id":"550e8400-e29b-41d4-a716-446655440000","weight":50,"voice_id":"9Godp7dNohUvXk6qp0gS","name":"Warm opener"},{"agent_id":"550e8400-e29b-41d4-a716-446655440000","weight":50,"name":"Direct opener"}]}}}},"responses":{"200":{"description":"Saved. The test, as GET answers it.","content":{"application/json":{"example":{"success":true,"data":{"variants":[{"id":"248bc6c9-d8e4-4e98-83d0-8bf9dbab1872","label":"A","position":1,"name":"Warm opener","agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Sofía"},"agent_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"agent_live_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"voice":{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center"},"weight":50,"promoted_at":null,"first_called_at":"2026-10-20T15:02:11.000Z","pinned_at":"2026-10-20T15:02:11.000Z","moved_at":null,"results":{"contacts":412,"attempted":398,"reached":241,"interested":57,"booked":31,"transferred":4,"calls":690,"answered_calls":251,"answer_rate":0.3638,"conversion_rate":0.1383}},{"id":"1f677692-f174-4dab-befc-4568ebc14452","label":"B","position":2,"name":"Direct opener","agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Sofía"},"agent_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"agent_live_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"voice":null,"weight":50,"promoted_at":null,"first_called_at":"2026-10-20T15:02:11.000Z","pinned_at":"2026-10-20T15:02:11.000Z","moved_at":null,"results":{"contacts":405,"attempted":398,"reached":241,"interested":41,"booked":31,"transferred":4,"calls":690,"answered_calls":251,"answer_rate":0.3638,"conversion_rate":0.1012}}],"promoted":false,"unassigned_contacts":128}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setCampaignVariants"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The test isn't valid: `details.reason` says why (`variant_count`: 0, 2 or 3 variants; `weights_total`: shares add up to 100; `agent_not_found` or `agent_unpublished`: an active agent of the workspace with a deployed version; `duplicate_treatment`: two variants call with the same agent and voice; `invalid_voice`, `voice_provider_mismatch`: a voice from GET /v1/voices that the agent's and the numbers' voice engine can speak).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key can't edit campaigns or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: A variant that has called keeps its agent and voice and can't be removed (`details.reason` `variant_called`, or `variant_dialing` while its first call is being placed): promote another variant instead.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/variants/{variant_id}/promote":{"post":{"operationId":"promoteCampaignVariant","summary":"Promote a variant","description":"Ends the test in the variant's favor, as Promote does: it gets every contact that enters the campaign from now on (100%), the others none. Contacts already in a variant keep it, with their retries and callbacks, and from then on every variant's calls follow its agent's live version.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's own rules as the member who created the key: that member needs a role that edits campaigns (owner, admin, builder or operator). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#promoteCampaignVariant"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"variant_id","in":"path","required":true,"description":"The variant id, from GET /v1/campaigns/{id}/variants.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Promoted. The test, as GET answers it.","content":{"application/json":{"example":{"success":true,"data":{"variants":[{"id":"248bc6c9-d8e4-4e98-83d0-8bf9dbab1872","label":"A","position":1,"name":"Warm opener","agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Sofía"},"agent_version":null,"agent_live_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"voice":{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center"},"weight":100,"promoted_at":"2026-10-26T18:00:00.000Z","first_called_at":"2026-10-20T15:02:11.000Z","pinned_at":"2026-10-20T15:02:11.000Z","moved_at":null,"results":{"contacts":412,"attempted":398,"reached":241,"interested":57,"booked":31,"transferred":4,"calls":690,"answered_calls":251,"answer_rate":0.3638,"conversion_rate":0.1383}},{"id":"1f677692-f174-4dab-befc-4568ebc14452","label":"B","position":2,"name":"Direct opener","agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Sofía"},"agent_version":null,"agent_live_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"voice":null,"weight":0,"promoted_at":null,"first_called_at":"2026-10-20T15:02:11.000Z","pinned_at":"2026-10-20T15:02:11.000Z","moved_at":null,"results":{"contacts":405,"attempted":398,"reached":241,"interested":41,"booked":31,"transferred":4,"calls":690,"answered_calls":251,"answer_rate":0.3638,"conversion_rate":0.1012}}],"promoted":true,"unassigned_contacts":128}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"promoteCampaignVariant"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key can't edit campaigns or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No campaign or variant with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/variants/{variant_id}/use-live-version":{"post":{"operationId":"useCampaignVariantLiveVersion","summary":"Move a variant to its agent's live version","description":"From now on the variant's calls use its agent's live (deployed) version instead of the version pinned when it began calling, as \"Use the live version\" does: deploying an agent alone leaves a running test's variant on its pinned version. The variant's results mark since when (`pinned_at`). `moved` is false, with a `reason`, when there was nothing to move: `not_pinned` (it hasn't called yet), `already_live` or `promoted` (a promoted test already follows the live version).\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's own rules as the member who created the key: that member needs a role that edits campaigns (owner, admin, builder or operator). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#useCampaignVariantLiveVersion"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"variant_id","in":"path","required":true,"description":"The variant id, from GET /v1/campaigns/{id}/variants.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The test, as GET answers it, with `moved` and `reason`.","content":{"application/json":{"example":{"success":true,"data":{"variants":[{"id":"248bc6c9-d8e4-4e98-83d0-8bf9dbab1872","label":"A","position":1,"name":"Warm opener","agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Sofía"},"agent_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"agent_live_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"voice":{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center"},"weight":50,"promoted_at":null,"first_called_at":"2026-10-20T15:02:11.000Z","pinned_at":"2026-10-20T15:02:11.000Z","moved_at":null,"results":{"contacts":412,"attempted":398,"reached":241,"interested":57,"booked":31,"transferred":4,"calls":690,"answered_calls":251,"answer_rate":0.3638,"conversion_rate":0.1383}},{"id":"1f677692-f174-4dab-befc-4568ebc14452","label":"B","position":2,"name":"Direct opener","agent":{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Sofía"},"agent_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"agent_live_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v1.4"},"voice":null,"weight":50,"promoted_at":null,"first_called_at":"2026-10-20T15:02:11.000Z","pinned_at":"2026-10-20T15:02:11.000Z","moved_at":null,"results":{"contacts":405,"attempted":398,"reached":241,"interested":41,"booked":31,"transferred":4,"calls":690,"answered_calls":251,"answer_rate":0.3638,"conversion_rate":0.1012}}],"promoted":false,"unassigned_contacts":128,"moved":true,"reason":null}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"useCampaignVariantLiveVersion"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key can't edit campaigns or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No campaign or variant with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/stages":{"get":{"operationId":"listCampaignStages","summary":"List a campaign's stages","description":"The campaign's pipeline stages in their order, as its Pipeline settings list them. An enrollment's `stage_id` says which one a contact is in; PATCH /v1/campaigns/{id}/enrollments/{enrollment_id} moves it. Every pipeline has at least one success and one failure end stage.\n\nRequires the `campaigns:read` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#listCampaignStages"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The stages, first to last.","content":{"application/json":{"example":{"success":true,"data":[{"id":"8ccfec1e-3750-4ce2-97f9-b81c6a4c1c04","name":"New","position":0,"color":"#64748b","is_terminal":false,"terminal_type":null,"created_at":"2026-10-01T16:20:00.000Z"},{"id":"07421f9c-cb6c-4b43-9d83-905f64782959","name":"Interested","position":3,"color":"#22c55e","is_terminal":true,"terminal_type":"success","created_at":"2026-10-01T16:20:00.000Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createCampaignStage","summary":"Add a stage","description":"Adds a stage after the campaign's last one, as Add stage does in its Pipeline settings. PUT /v1/campaigns/{id}/stages/order moves it.\n\nRequires the `campaigns:write` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#createCampaignStage"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The stage's name.","type":"string","minLength":1,"maxLength":80},"color":{"description":"A hex color, such as `#22c55e` (default `#64748b`).","type":"string"},"is_terminal":{"description":"An end stage: a contact's way through the pipeline ends there, well or badly.","type":"boolean"},"terminal_type":{"description":"For an end stage: `success` or `failure`.","type":["string","null"],"enum":["success","failure",null]}},"required":["name"]},"example":{"name":"Interested","color":"#22c55e","is_terminal":true,"terminal_type":"success"}}}},"responses":{"201":{"description":"Added.","content":{"application/json":{"example":{"success":true,"data":{"id":"07421f9c-cb6c-4b43-9d83-905f64782959","name":"Interested","position":3,"color":"#22c55e","is_terminal":true,"terminal_type":"success","created_at":"2026-10-01T16:20:00.000Z"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/stages/{stage_id}":{"patch":{"operationId":"updateCampaignStage","summary":"Change a stage","description":"Renames a stage, changes its color, or makes it an end stage (`is_terminal` with `terminal_type`) or a step on the way (`is_terminal: false`). Only the fields sent change.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#updateCampaignStage"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"stage_id","in":"path","required":true,"description":"The stage id, from GET /v1/campaigns/{id}/stages.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The stage's name.","type":"string","minLength":1,"maxLength":80},"color":{"description":"A hex color, such as `#22c55e` (default `#64748b`).","type":"string"},"is_terminal":{"description":"An end stage: a contact's way through the pipeline ends there, well or badly.","type":"boolean"},"terminal_type":{"description":"For an end stage: `success` or `failure`.","type":["string","null"],"enum":["success","failure",null]}}},"example":{"name":"Booked"}}}},"responses":{"200":{"description":"Changed.","content":{"application/json":{"example":{"success":true,"data":{"id":"07421f9c-cb6c-4b43-9d83-905f64782959","name":"Booked","position":3,"color":"#22c55e","is_terminal":true,"terminal_type":"success","created_at":"2026-10-01T16:20:00.000Z"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateCampaignStage"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign or stage with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: It would leave the pipeline without a success or a failure end stage (`details.reason` `LAST_END_STAGE`): every pipeline keeps at least one of each.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteCampaignStage","summary":"Delete a stage","description":"Deletes a stage no contact is in and no workflow step moves contacts into. Only a key created by an owner or admin may, as only they delete stages in the dashboard.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#deleteCampaignStage"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"stage_id","in":"path","required":true,"description":"The stage id, from GET /v1/campaigns/{id}/stages.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"07421f9c-cb6c-4b43-9d83-905f64782959","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteCampaignStage"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key wasn't created by an owner or admin, its creator has left the workspace, or it is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No campaign or stage with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: It would leave the pipeline without a success or a failure end stage (`details.reason` `LAST_END_STAGE`): every pipeline keeps at least one of each. `conflict`: Contacts are in the stage (`details.reason` `STAGE_HAS_CONTACTS`, `details.contacts`): move them first. Or a workflow step moves contacts into it (`STAGE_IN_WORKFLOW`, `details.steps`): change that step first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/stages/order":{"put":{"operationId":"reorderCampaignStages","summary":"Reorder a campaign's stages","description":"Puts the stages in a new order, as dragging them does in the Pipeline settings: `stage_ids` lists every stage of the campaign exactly once, first to last. Nothing is written when it doesn't.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#reorderCampaignStages"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stage_ids":{"description":"Every stage of the campaign, in the new order.","type":"array","minItems":1,"maxItems":200,"items":{"description":"A stage id.","type":"string","format":"uuid"}}},"required":["stage_ids"]},"example":{"stage_ids":["8ccfec1e-3750-4ce2-97f9-b81c6a4c1c04","07421f9c-cb6c-4b43-9d83-905f64782959"]}}}},"responses":{"200":{"description":"The stages in their new order.","content":{"application/json":{"example":{"success":true,"data":[{"id":"8ccfec1e-3750-4ce2-97f9-b81c6a4c1c04","name":"New","position":0,"color":"#64748b","is_terminal":false,"terminal_type":null,"created_at":"2026-10-01T16:20:00.000Z"},{"id":"07421f9c-cb6c-4b43-9d83-905f64782959","name":"Interested","position":3,"color":"#22c55e","is_terminal":true,"terminal_type":"success","created_at":"2026-10-01T16:20:00.000Z"}]}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"reorderCampaignStages"}}}}}},"400":{"description":"`validation_error`: The body is invalid, or `stage_ids` doesn't list every stage of the campaign exactly once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/inbound-channel":{"get":{"operationId":"getCampaignInboundChannel","summary":"Get a campaign's inbound number","description":"The phone channel whose callers join the campaign as contacts (the inbound number in the campaign's settings), or `channel: null`.\n\nRequires the `campaigns:read` scope.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#getCampaignInboundChannel"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The inbound number.","content":{"application/json":{"example":{"success":true,"data":{"campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Sales line","phone_number":"+15125550100"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"setCampaignInboundChannel","summary":"Set a campaign's inbound number","description":"Sets the phone channel whose callers join the campaign as contacts, as \"Add people who call this number\" does in its settings: while the campaign is active, a caller to it is enrolled and goes through its steps. `channel_id: null` unlinks it, and that is never held for approval (it stops callers joining). A channel another campaign already takes callers from is refused; unlink it there first. A campaign takes callers from one number: the one set here replaces the one it had.\n\nRequires the `campaigns:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Campaigns"],"x-required-scope":"campaigns:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#setCampaignInboundChannel"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel_id":{"description":"A phone channel of the workspace (GET /v1/channels), or null to unlink.","type":["string","null"],"format":"uuid"}},"required":["channel_id"]},"example":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64"}}}},"responses":{"200":{"description":"Set. The inbound number, as GET answers it.","content":{"application/json":{"example":{"success":true,"data":{"campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","channel":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Sales line","phone_number":"+15125550100"}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setCampaignInboundChannel"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key lacks `channels:write`, or the member who created it can't change channels (owner, admin or builder) or has left the workspace, or it is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No campaign or phone channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: Another campaign already takes this number's callers (`details.reason` `CHANNEL_LINKED_TO_ANOTHER_CAMPAIGN`, `details.campaign_id`), or the campaign's inbound number changed while this request ran (`details.reason` `INBOUND_CHANNEL_CHANGED`): nothing was written, so read it again and retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/campaigns/{id}/analytics":{"get":{"operationId":"getCampaignAnalytics","summary":"Get a campaign's analytics","description":"The campaign's results as its Analytics page shows them: its contacts by answer (`lead_responses`), its calls by outcome, the answer rate (answered calls per call) and the conversion rate (interested or booked contacts per contact), the WhatsApp messages its steps sent, and the contacts added per day in the campaign's timezone. `range` limits the calls, the messages and the daily counts to the last 7, 30 or 90 days (the daily counts cover the last 30 days with `all`); the contact counts are the campaign's totals.\n\nRequires the `campaigns:read` scope.\n\nThe call figures (`call_outcomes`, `total_calls`, `answered_calls`, `voicemail_calls`, `answer_rate`) count every call of the campaign, so they are answered only to a key that may read every call (`calls:read`), as the dashboard shows them only to members who see every call. For any other key they are null; the contact, message and conversion figures are always answered.","tags":["Campaigns"],"x-required-scope":"campaigns:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/campaigns#getCampaignAnalytics"},"parameters":[{"name":"id","in":"path","required":true,"description":"The campaign id.","schema":{"type":"string","format":"uuid"}},{"name":"range","in":"query","required":false,"description":"The period: `7d`, `30d`, `90d` or `all`.","schema":{"type":"string","enum":["7d","30d","90d","all"],"default":"all"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The analytics.","content":{"application/json":{"example":{"success":true,"data":{"campaign_id":"f8d1c003-b292-448d-b8d8-1dd521b94b3b","range":"30d","total_enrollments":945,"active_enrollments":212,"completed_enrollments":690,"failed_enrollments":43,"lead_responses":{"unknown":501,"interested":98,"not_interested":214,"callback_requested":37,"appointment_booked":72,"do_not_contact":23},"call_outcomes":{"answered":492,"no_answer":610,"voicemail":288,"busy":41,"failed":19},"total_calls":1450,"answered_calls":492,"voicemail_calls":288,"total_messages":310,"answer_rate":0.3393,"conversion_rate":0.1799,"enrollments_by_day":[{"date":"2026-10-24","count":38},{"date":"2026-10-25","count":12}]}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No campaign with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversions":{"get":{"operationId":"listConversions","summary":"List conversions","description":"The workspace's conversions, the most recently recorded first: bookings, visits, calls, goals and won deals NuTalk recorded itself, and what the API recorded. Each event has a key that never changes, who recorded it, its proof and where the person came from.\n\nRequires the `conversions:read` scope.\n\nFields: `id`, `name`, `key`, `occurred_at`, `recorded_at`, `value`, `contact_id`, `source`, `proof`, `touch`, `test`, `verification`, `retracted_at`, and `currency`, `updated_at`, `retracted_reason`.\n\nOnly the workspace's owners and admins give a key `conversions:read`, as only they read conversions in NuTalk.","tags":["Conversions"],"x-required-scope":"conversions:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversions#listConversions"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"name","in":"query","required":false,"description":"Only events of this name.","schema":{"type":"string","enum":["lead.new","lead.qualified","appointment.booked","appointment.confirmed","appointment.attended","appointment.no_show","appointment.cancelled","call.connected","call.transferred","goal.reached","deal.won","payment.received","outcome.custom"]}},{"name":"contact_id","in":"query","required":false,"description":"Only this contact's events.","schema":{"type":"string","format":"uuid"}},{"name":"key","in":"query","required":false,"description":"The event with this key (an API event's is `api:<Idempotency-Key>`).","schema":{"type":"string","maxLength":200}},{"name":"retracted","in":"query","required":false,"description":"`false`: only live events; `true`: only events taken back. Both without it.","schema":{"type":"boolean"}},{"name":"test","in":"query","required":false,"description":"`false`: no test events; `true`: only them. Both without it.","schema":{"type":"boolean"}},{"name":"recorded_after","in":"query","required":false,"description":"Only events recorded at or after this time.","schema":{"type":"string","format":"date-time"}},{"name":"recorded_before","in":"query","required":false,"description":"Only events recorded before this time.","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of events. Each has the fields below.","content":{"application/json":{"example":{"success":true,"data":[{"id":"6e2a9c4f-1b7d-4e3a-8c5f-0d9b7a6e4c21","name":"payment.received","key":"api:sale-1001","occurred_at":"2026-10-25T23:42:10+00:00","recorded_at":"2026-10-25T23:42:11.204+00:00","updated_at":"2026-10-25T23:42:11.204+00:00","value":650,"currency":"MXN","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","source":{"kind":"api","agent_version_id":null,"user_id":null,"api_key_id":"7b7a0ce4-0000-4000-8000-000000000001","channel_id":null,"conversation_id":null,"call_id":null},"proof":{"type":"api","id":"7b7a0ce4-0000-4000-8000-000000000001"},"touch":{"first":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}},"last":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}}},"test":false,"verification":"verified","retracted_at":null,"retracted_reason":null},{"id":"7f3b0d5a-2c8e-4f4b-9d6a-1e0c8b7f5d32","name":"appointment.booked","key":"clinic_appointment:6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e:booked","occurred_at":"2026-10-25T23:42:10+00:00","recorded_at":"2026-10-25T23:42:11.204+00:00","updated_at":"2026-10-25T23:42:11.204+00:00","value":null,"currency":null,"contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","source":{"kind":"ai_agent","agent_version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","user_id":null,"api_key_id":null,"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","call_id":null},"proof":{"appointment_id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","status":"scheduled","type":"clinic_appointment","id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e"},"touch":{"first":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}},"last":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}}},"test":false,"verification":"verified","retracted_at":null,"retracted_reason":null}],"pagination":{"total":2,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Conversions is off for the workspace (`details.reason` `conversions_off`): NuTalk switches it on per workspace during the beta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"recordConversion","summary":"Record a conversion","description":"Records an outcome NuTalk can't see by itself: a qualified lead, a won deal, a payment received (from your POS or your Stripe) or a custom outcome of your own, about one of the workspace's contacts. Bookings, visits, calls and goals record themselves, also when the API books or marks them.\n\nRequires the `conversions:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe Idempotency-Key is required, and it becomes the event's key `api:<Idempotency-Key>`, the id ad platforms will receive (Meta's event_id, Google's transactionId, TikTok's event_id), so use your own id for it: the payment's, the deal's. It answers the same event forever, not only for 24 hours: a retry with the same details gets `200` with the event as it is (retracted too: the API never brings back what a teammate took back), other details get `409`. Don't put personal data in it.\n\nA recorded event is sent to the endpoints that name `conversion.recorded` (and, later, to the ad platforms the workspace connects), unless it is a test.","tags":["Conversions"],"x-required-scope":"conversions:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversions#recordConversion"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"What happened.","type":"string","enum":["lead.qualified","deal.won","payment.received","outcome.custom"]},"contact_id":{"description":"The contact it is about (find or create them with the contacts endpoints).","type":"string","format":"uuid"},"occurred_at":{"description":"When it happened (now when left out): at most 365 days ago, never in the future.","type":"string","format":"date-time"},"value":{"description":"Its value, at most 2 decimals, with `currency`. Required for `payment.received`.","type":"number","minimum":0,"maximum":999999999999.99},"currency":{"description":"ISO 4217 code of `value` (MXN, USD), with it.","type":"string","minLength":3,"maxLength":3},"outcome":{"description":"For `outcome.custom` only, and required there: your own name for it, in snake_case (`quote_sent`), up to 64 characters.","type":"string","maxLength":64},"conversation_id":{"description":"The conversation it happened in, if any: its channel and touch go with the event.","type":"string","format":"uuid"},"call_id":{"description":"The call it happened in, if any.","type":"string","format":"uuid"},"test":{"description":"Record it as a test: it shows in this list and is never sent. An outcome of a test chat or a test call is a test anyway.","type":"boolean","default":false}},"required":["name","contact_id"]},"example":{"name":"payment.received","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","occurred_at":"2026-10-25T17:42:10-06:00","value":650,"currency":"MXN"}}}},"responses":{"200":{"description":"This Idempotency-Key already recorded this event, with these details: the event as it is.","content":{"application/json":{"example":{"success":true,"data":{"id":"6e2a9c4f-1b7d-4e3a-8c5f-0d9b7a6e4c21","name":"payment.received","key":"api:sale-1001","occurred_at":"2026-10-25T23:42:10+00:00","recorded_at":"2026-10-25T23:42:11.204+00:00","updated_at":"2026-10-25T23:42:11.204+00:00","value":650,"currency":"MXN","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","source":{"kind":"api","agent_version_id":null,"user_id":null,"api_key_id":"7b7a0ce4-0000-4000-8000-000000000001","channel_id":null,"conversation_id":null,"call_id":null},"proof":{"type":"api","id":"7b7a0ce4-0000-4000-8000-000000000001"},"touch":{"first":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}},"last":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}}},"test":false,"verification":"verified","retracted_at":null,"retracted_reason":null}}}}},"201":{"description":"The event was recorded.","content":{"application/json":{"example":{"success":true,"data":{"id":"6e2a9c4f-1b7d-4e3a-8c5f-0d9b7a6e4c21","name":"payment.received","key":"api:sale-1001","occurred_at":"2026-10-25T23:42:10+00:00","recorded_at":"2026-10-25T23:42:11.204+00:00","updated_at":"2026-10-25T23:42:11.204+00:00","value":650,"currency":"MXN","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","source":{"kind":"api","agent_version_id":null,"user_id":null,"api_key_id":"7b7a0ce4-0000-4000-8000-000000000001","channel_id":null,"conversation_id":null,"call_id":null},"proof":{"type":"api","id":"7b7a0ce4-0000-4000-8000-000000000001"},"touch":{"first":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}},"last":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}}},"test":false,"verification":"verified","retracted_at":null,"retracted_reason":null}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The Idempotency-Key header is missing, or isn't 1 to 150 letters, digits and . _ : - (`details.reason` `idempotency_key_required`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Conversions is off for the workspace (`details.reason` `conversions_off`): NuTalk switches it on per workspace during the beta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No contact (or conversation, or call) with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The Idempotency-Key already recorded a conversion with other details (`details.reason` `idempotency_key_reused`, `details.fields`, `details.conversion_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: The workspace recorded 600 conversions and touches through the API in the last hour, or 60 in the last minute (on top of its general limit).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/conversions/{id}":{"get":{"operationId":"getConversion","summary":"Get a conversion","description":"One of the workspace's conversions, as it is now (taken back or not).\n\nRequires the `conversions:read` scope.","tags":["Conversions"],"x-required-scope":"conversions:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/conversions#getConversion"},"parameters":[{"name":"id","in":"path","required":true,"description":"The conversion id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The event.","content":{"application/json":{"example":{"success":true,"data":{"id":"6e2a9c4f-1b7d-4e3a-8c5f-0d9b7a6e4c21","name":"payment.received","key":"api:sale-1001","occurred_at":"2026-10-25T23:42:10+00:00","recorded_at":"2026-10-25T23:42:11.204+00:00","updated_at":"2026-10-25T23:42:11.204+00:00","value":650,"currency":"MXN","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","source":{"kind":"api","agent_version_id":null,"user_id":null,"api_key_id":"7b7a0ce4-0000-4000-8000-000000000001","channel_id":null,"conversation_id":null,"call_id":null},"proof":{"type":"api","id":"7b7a0ce4-0000-4000-8000-000000000001"},"touch":{"first":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}},"last":{"id":"8a4c1e6b-3d9f-4a5c-8e7b-2f1d9c8a6e43","at":"2026-10-23T23:17:02+00:00","channel":"whatsapp_api","platform":"meta","kind":"ad","ad":{"id":"120211000000000001","headline":"Primera consulta"},"click_ids":{"ctwa_clid":"example-click-id"}}},"test":false,"verification":"verified","retracted_at":null,"retracted_reason":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Conversions is off for the workspace (`details.reason` `conversions_off`): NuTalk switches it on per workspace during the beta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No conversion with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/goals":{"get":{"operationId":"listGoals","summary":"List goals","description":"Lists evaluation goals with their targets, newest first.\n\nRequires the `goals:read` scope.","tags":["Goals"],"x-required-scope":"goals:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/goals#listGoals"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"agent_id","in":"query","required":false,"description":"Only this agent's goals.","schema":{"type":"string","format":"uuid"}},{"name":"channel_id","in":"query","required":false,"description":"Only goals evaluated on this channel.","schema":{"type":"string","format":"uuid"}},{"name":"active","in":"query","required":false,"description":"Only active (true) or inactive (false) goals.","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of goals.","content":{"application/json":{"example":{"success":true,"data":[{"id":"7b7b8def-a340-4bfd-b164-779fa5843735","agent_id":"550e8400-e29b-41d4-a716-446655440000","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","title":"Book qualified meeting","description":"The call ends with a confirmed meeting time.","active":true,"created_at":"2026-10-02T02:25:39.039843+00:00","updated_at":"2026-10-02T02:25:39.039843+00:00","goal_targets":[{"id":"0d89e7b2-6025-4ce7-8733-033c2f74fd72","period":"week","target_type":"count","target_count":25,"target_rate":null,"is_primary":true,"created_at":"2026-10-02T02:25:39.044751+00:00","updated_at":"2026-10-02T02:25:39.044751+00:00"}]}],"pagination":{"total":1,"limit":10,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createGoal","summary":"Create a goal","description":"Creates a binary success goal for an agent, evaluated on one of the agent's channels. After each evaluated conversation the evaluator records whether the goal was achieved; targets set how many (or what share) per period count as on track.\n\nRequires the `goals:write` scope.","tags":["Goals"],"x-required-scope":"goals:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/goals#createGoal"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"description":"The agent the goal belongs to.","type":"string","format":"uuid"},"channel_id":{"description":"A channel the agent is on. Optional when the agent is on exactly one channel (that one is used); required when it is on several (the error lists them).","type":["string","null"],"format":"uuid"},"title":{"description":"Short name.","type":"string","minLength":1,"maxLength":200},"description":{"description":"What success looks like: the evaluator judges each conversation against it.","type":"string","minLength":1,"maxLength":5000},"active":{"description":"Inactive goals are not evaluated.","type":"boolean","default":true},"targets":{"description":"Period targets.","type":"array","items":{"description":"A target.","type":"object","properties":{"period":{"description":"The period the target counts over.","type":"string","enum":["day","week","month","quarter","semester","year"]},"target_type":{"description":"`count`: a number of achieved conversations per period; `rate`: a percentage of evaluated conversations.","type":"string","enum":["count","rate"]},"target_count":{"description":"Required for `count` targets (and only for them).","type":["integer","null"],"minimum":1},"target_rate":{"description":"Required for `rate` targets (and only for them): a percentage above 0, at most 100.","type":["number","null"],"maximum":100},"is_primary":{"description":"The target shown first.","type":"boolean","default":false}},"required":["period","target_type"]}}},"required":["agent_id","title","description"]},"example":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","title":"Book qualified meeting","description":"The call ends with a confirmed meeting time.","targets":[{"period":"week","target_type":"count","target_count":25,"is_primary":true}]}}}},"responses":{"201":{"description":"The goal.","content":{"application/json":{"example":{"success":true,"data":{"id":"7b7b8def-a340-4bfd-b164-779fa5843735","agent_id":"550e8400-e29b-41d4-a716-446655440000","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","title":"Book qualified meeting","description":"The call ends with a confirmed meeting time.","active":true,"created_at":"2026-10-02T02:25:39.039843+00:00","updated_at":"2026-10-02T02:25:39.039843+00:00","goal_targets":[{"id":"0d89e7b2-6025-4ce7-8733-033c2f74fd72","period":"week","target_type":"count","target_count":25,"target_rate":null,"is_primary":true,"created_at":"2026-10-02T02:25:39.044751+00:00","updated_at":"2026-10-02T02:25:39.044751+00:00"}]}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The agent is on no channel or on several and `channel_id` is missing (`details.bound_channel_ids`), or `channel_id` is not one of the agent's channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same). `not_found`: `channel_id` is not a channel of your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The agent already has a goal with this title on this channel.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/goals/{id}":{"get":{"operationId":"getGoal","summary":"Get a goal","description":"Returns one goal with its targets.\n\nRequires the `goals:read` scope.","tags":["Goals"],"x-required-scope":"goals:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/goals#getGoal"},"parameters":[{"name":"id","in":"path","required":true,"description":"The goal id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The goal.","content":{"application/json":{"example":{"success":true,"data":{"id":"7b7b8def-a340-4bfd-b164-779fa5843735","agent_id":"550e8400-e29b-41d4-a716-446655440000","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","title":"Book qualified meeting","description":"The call ends with a confirmed meeting time.","active":true,"created_at":"2026-10-02T02:25:39.039843+00:00","updated_at":"2026-10-02T02:25:39.039843+00:00","goal_targets":[{"id":"0d89e7b2-6025-4ce7-8733-033c2f74fd72","period":"week","target_type":"count","target_count":25,"target_rate":null,"is_primary":true,"created_at":"2026-10-02T02:25:39.044751+00:00","updated_at":"2026-10-02T02:25:39.044751+00:00"}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No goal with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateGoal","summary":"Update a goal","description":"Changes the fields you send. `targets` replaces every target of the goal. `channel_id` may move the goal to another of the agent's channels; it cannot be set to null.\n\nRequires the `goals:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Goals"],"x-required-scope":"goals:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/goals#updateGoal"},"parameters":[{"name":"id","in":"path","required":true,"description":"The goal id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"description":"New title.","type":"string","minLength":1,"maxLength":200},"description":{"description":"New success criteria.","type":"string","minLength":1,"maxLength":5000},"channel_id":{"description":"Another channel the agent is on.","type":"string","format":"uuid"},"active":{"description":"Turn evaluation on or off.","type":"boolean"},"targets":{"description":"The goal's new targets (replaces all).","type":"array","items":{"description":"A target.","type":"object","properties":{"period":{"description":"The period the target counts over.","type":"string","enum":["day","week","month","quarter","semester","year"]},"target_type":{"description":"`count`: a number of achieved conversations per period; `rate`: a percentage of evaluated conversations.","type":"string","enum":["count","rate"]},"target_count":{"description":"Required for `count` targets (and only for them).","type":["integer","null"],"minimum":1},"target_rate":{"description":"Required for `rate` targets (and only for them): a percentage above 0, at most 100.","type":["number","null"],"maximum":100},"is_primary":{"description":"The target shown first.","type":"boolean","default":false}},"required":["period","target_type"]}}}},"example":{"active":true,"targets":[{"period":"month","target_type":"rate","target_rate":70,"is_primary":true}]}}}},"responses":{"200":{"description":"The updated goal.","content":{"application/json":{"example":{"success":true,"data":{"id":"7b7b8def-a340-4bfd-b164-779fa5843735","agent_id":"550e8400-e29b-41d4-a716-446655440000","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","title":"Book qualified meeting","description":"The call ends with a confirmed meeting time.","active":true,"created_at":"2026-10-02T02:25:39.039843+00:00","updated_at":"2026-10-02T02:25:46.779756+00:00","goal_targets":[{"id":"0d89e7b2-6025-4ce7-8733-033c2f74fd72","period":"month","target_type":"rate","target_count":null,"target_rate":70,"is_primary":true,"created_at":"2026-10-02T02:25:46.792433+00:00","updated_at":"2026-10-02T02:25:46.792433+00:00"}]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateGoal"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent, or `channel_id` is null or not one of the agent's channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No goal with this id in your workspace (ids of other workspaces answer the same). `not_found`: `channel_id` is not a channel of your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The agent already has a goal with this title on this channel.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteGoal","summary":"Delete a goal","description":"Deletes the goal and its targets.\n\nRequires the `goals:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Goals"],"x-required-scope":"goals:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/goals#deleteGoal"},"parameters":[{"name":"id","in":"path","required":true,"description":"The goal id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The goal was deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"7b7b8def-a340-4bfd-b164-779fa5843735","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteGoal"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No goal with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/competencies":{"get":{"operationId":"listCompetencies","summary":"List competencies","description":"Lists competencies with their performance levels, newest first.\n\nRequires the `competencies:read` scope.","tags":["Competencies"],"x-required-scope":"competencies:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/competencies#listCompetencies"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"agent_id","in":"query","required":false,"description":"Only this agent's competencies.","schema":{"type":"string","format":"uuid"}},{"name":"category","in":"query","required":false,"description":"Only this type.","schema":{"type":"string","enum":["skill","knowledge","attitude"]}},{"name":"active","in":"query","required":false,"description":"Only active (true) or inactive (false) competencies.","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of competencies.","content":{"application/json":{"example":{"success":true,"data":[{"id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","agent_id":"550e8400-e29b-41d4-a716-446655440000","name":"Handles price questions","category":"skill","area":"Sales","description":"Gives the approved price range and offers to book, without pressure.","rubric":[{"level":"Needs improvement","description":"Invents a price, dodges the question or pushes for the sale.","criteria":null,"weight":null,"threshold":null},{"level":"Meets expectations","description":"Gives the approved range and offers the next step.","criteria":null,"weight":null,"threshold":null},{"level":"Exceeds expectations","description":"Also explains what's included and answers the next question before it's asked.","criteria":null,"weight":null,"threshold":null}],"min_levels":3,"weight":1,"active":true,"created_at":"2026-10-02T02:25:39.506247+00:00","updated_at":"2026-10-02T02:25:39.506247+00:00"}],"pagination":{"total":1,"limit":10,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createCompetency","summary":"Create a competency","description":"Creates a competency the evaluator rates in each of the agent's evaluated conversations: what good looks like and 3 or 4 performance levels. Every competency counts the same.\n\nRequires the `competencies:write` scope.\n\nLevels go from the lowest to the highest. The middle level (the third of four) is the passing level: an evaluation at or above it counts as passing.","tags":["Competencies"],"x-required-scope":"competencies:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/competencies#createCompetency"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"description":"The agent it rates.","type":"string","format":"uuid"},"name":{"description":"Competency name: one behavior, e.g. Handles price questions.","type":"string","minLength":1,"maxLength":200},"description":{"description":"What good looks like, in one sentence the evaluator can check in a transcript.","type":["string","null"],"maxLength":5000},"category":{"description":"The competency's type; null for none.","type":["string","null"],"enum":["skill","knowledge","attitude",null],"default":"skill"},"area":{"description":"The competency's category: the group it belongs to, any name (e.g. Sales, Customer service, Technical); null for none.","type":["string","null"],"maxLength":60},"rubric":{"description":"Performance levels, from the lowest to the highest.","type":"array","minItems":3,"maxItems":4,"items":{"description":"A level.","type":"object","properties":{"level":{"description":"Level name, e.g. Meets expectations.","type":"string","minLength":1,"maxLength":100},"description":{"description":"What this level looks like in a conversation: one sentence.","type":["string","null"],"maxLength":1000},"criteria":{"description":"Optional extra detail for the evaluator; levels written in the dashboard keep everything in `description`.","type":["string","null"],"maxLength":2000},"weight":{"description":"Accepted and ignored; answered as null.","type":["number","null"],"deprecated":true},"threshold":{"description":"Accepted and ignored; answered as null.","type":["number","null"],"deprecated":true}},"required":["level"]}},"min_levels":{"description":"Accepted and ignored: a rubric has 3 or 4 levels.","type":"integer","deprecated":true},"weight":{"description":"Accepted and ignored: every competency counts the same. Answered as 1.","type":"number","deprecated":true},"active":{"description":"Inactive competencies are not evaluated.","type":"boolean","default":true}},"required":["agent_id","name","rubric"]},"example":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","name":"Handles price questions","description":"Gives the approved price range and offers to book, without pressure.","category":"skill","area":"Sales","rubric":[{"level":"Needs improvement","description":"Invents a price, dodges the question or pushes for the sale."},{"level":"Meets expectations","description":"Gives the approved range and offers the next step."},{"level":"Exceeds expectations","description":"Also explains what's included and answers the next question before it's asked."}]}}}},"responses":{"201":{"description":"The competency.","content":{"application/json":{"example":{"success":true,"data":{"id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","agent_id":"550e8400-e29b-41d4-a716-446655440000","name":"Handles price questions","category":"skill","area":"Sales","description":"Gives the approved price range and offers to book, without pressure.","rubric":[{"level":"Needs improvement","description":"Invents a price, dodges the question or pushes for the sale.","criteria":null,"weight":null,"threshold":null},{"level":"Meets expectations","description":"Gives the approved range and offers the next step.","criteria":null,"weight":null,"threshold":null},{"level":"Exceeds expectations","description":"Also explains what's included and answers the next question before it's asked.","criteria":null,"weight":null,"threshold":null}],"min_levels":3,"weight":1,"active":true,"created_at":"2026-10-02T02:25:39.506247+00:00","updated_at":"2026-10-02T02:25:39.506247+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The rubric doesn't have 3 or 4 levels, or a level has no name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error`: `area`, or a null `category`, sent before the workspace's next platform update makes them available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/competencies/{id}":{"get":{"operationId":"getCompetency","summary":"Get a competency","description":"Returns one competency with its performance levels.\n\nRequires the `competencies:read` scope.","tags":["Competencies"],"x-required-scope":"competencies:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/competencies#getCompetency"},"parameters":[{"name":"id","in":"path","required":true,"description":"The competency id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The competency.","content":{"application/json":{"example":{"success":true,"data":{"id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","agent_id":"550e8400-e29b-41d4-a716-446655440000","name":"Handles price questions","category":"skill","area":"Sales","description":"Gives the approved price range and offers to book, without pressure.","rubric":[{"level":"Needs improvement","description":"Invents a price, dodges the question or pushes for the sale.","criteria":null,"weight":null,"threshold":null},{"level":"Meets expectations","description":"Gives the approved range and offers the next step.","criteria":null,"weight":null,"threshold":null},{"level":"Exceeds expectations","description":"Also explains what's included and answers the next question before it's asked.","criteria":null,"weight":null,"threshold":null}],"min_levels":3,"weight":1,"active":true,"created_at":"2026-10-02T02:25:39.506247+00:00","updated_at":"2026-10-02T02:25:39.506247+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No competency with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateCompetency","summary":"Update a competency","description":"Changes the fields you send; `rubric` replaces the whole rubric.\n\nRequires the `competencies:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nLevels go from the lowest to the highest. The middle level (the third of four) is the passing level: an evaluation at or above it counts as passing.","tags":["Competencies"],"x-required-scope":"competencies:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/competencies#updateCompetency"},"parameters":[{"name":"id","in":"path","required":true,"description":"The competency id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"New name.","type":"string","minLength":1,"maxLength":200},"description":{"description":"What good looks like.","type":["string","null"],"maxLength":5000},"category":{"description":"The type; null for none.","type":["string","null"],"enum":["skill","knowledge","attitude",null]},"area":{"description":"The category; null for none.","type":["string","null"],"maxLength":60},"rubric":{"description":"New levels, from the lowest to the highest (replaces all).","type":"array","minItems":3,"maxItems":4,"items":{"description":"A level.","type":"object","properties":{"level":{"description":"Level name, e.g. Meets expectations.","type":"string","minLength":1,"maxLength":100},"description":{"description":"What this level looks like in a conversation: one sentence.","type":["string","null"],"maxLength":1000},"criteria":{"description":"Optional extra detail for the evaluator; levels written in the dashboard keep everything in `description`.","type":["string","null"],"maxLength":2000},"weight":{"description":"Accepted and ignored; answered as null.","type":["number","null"],"deprecated":true},"threshold":{"description":"Accepted and ignored; answered as null.","type":["number","null"],"deprecated":true}},"required":["level"]}},"min_levels":{"description":"Accepted and ignored.","type":"integer","deprecated":true},"weight":{"description":"Accepted and ignored: every competency counts the same.","type":"number","deprecated":true},"active":{"description":"Turn evaluation on or off.","type":"boolean"}}},"example":{"area":"Sales","active":true}}}},"responses":{"200":{"description":"The updated competency.","content":{"application/json":{"example":{"success":true,"data":{"id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","agent_id":"550e8400-e29b-41d4-a716-446655440000","name":"Handles price questions","category":"skill","area":"Sales","description":"Gives the approved price range and offers to book, without pressure.","rubric":[{"level":"Needs improvement","description":"Invents a price, dodges the question or pushes for the sale.","criteria":null,"weight":null,"threshold":null},{"level":"Meets expectations","description":"Gives the approved range and offers the next step.","criteria":null,"weight":null,"threshold":null},{"level":"Exceeds expectations","description":"Also explains what's included and answers the next question before it's asked.","criteria":null,"weight":null,"threshold":null}],"min_levels":3,"weight":1,"active":true,"created_at":"2026-10-02T02:25:39.506247+00:00","updated_at":"2026-10-02T02:25:47.646176+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateCompetency"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent, or the new rubric doesn't have 3 or 4 levels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No competency with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error`: `area`, or a null `category`, sent before the workspace's next platform update makes them available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteCompetency","summary":"Delete a competency","description":"Deletes the competency.\n\nRequires the `competencies:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Competencies"],"x-required-scope":"competencies:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/competencies#deleteCompetency"},"parameters":[{"name":"id","in":"path","required":true,"description":"The competency id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The competency was deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteCompetency"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No competency with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels":{"get":{"operationId":"listChannels","summary":"List channels","description":"Lists the workspace's channels (phone numbers, WhatsApp numbers, Instagram and Messenger pages, website widgets, iMessage lines), newest first.\n\nRequires the `channels:read` scope.\n\nA WhatsApp sync channel reads `status: \"unknown\"` (and `health: \"warning\"`) while its sync service is not reporting, because its last known state cannot be trusted then; `status_detail` gives the `reason` (`bridge_offline` or `no_heartbeat`) and `since`, the last time it reported. It is `null` for every other channel.\n\nThe `status` filter matches the current status, so a channel that reads `unknown` is left out of it.\n\nA phone (`voice`) channel also has `language`, the language it speaks, `voice`, the voice it speaks with (`id` and `name`; change it with `PUT /v1/channels/:id/voice`), `receives_calls`, whether calls to its number ring it now (`inbound_enabled` is only the channel's own switch), and `incoming_calls_number`, the number whose calls ring it now: its own, or an earlier number still routed to it (`null` when none does). They are `null` on other channels, and the last two on a Zoom Phone channel, whose lines are set on its Zoom page. `phone_number_id` is its number's id, and `scheduling_mode` the scheduling system set on any channel (`null` when none was set; `PUT /v1/channels/:id/scheduling`).","tags":["Channels"],"x-required-scope":"channels:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#listChannels"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"type","in":"query","required":false,"description":"Only channels of this type.","schema":{"type":"string","enum":["whatsapp","instagram","messenger","voice","web_widget","imessage","live_room"]}},{"name":"status","in":"query","required":false,"description":"Only channels in this status.","schema":{"type":"string","enum":["disconnected","pending","connected","error"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of channels.","content":{"application/json":{"example":{"success":true,"data":[{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support Agent - Voice","type":"voice","status":"connected","health":"healthy","phone_number":"+525512345678","phone_number_id":"2f6d8b0a-4c1e-4f3a-9b5d-7e9f1a3c5b72","inbound_enabled":true,"voicemail_enabled":false,"messages_monthly":0,"active_conversations":0,"connected_at":"2026-10-02T02:23:10.004+00:00","created_at":"2026-10-02T02:23:10.004+00:00","updated_at":"2026-10-02T02:23:10.004+00:00","status_detail":null,"language":"es-MX","voice":{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center"},"receives_calls":true,"incoming_calls_number":"+525512345678","scheduling_mode":null}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createChannel","summary":"Create a phone channel","description":"Puts an agent on one of the workspace's numbers with a voice of the list, as the dashboard's new phone channel does: the agent calls from the number and answers its incoming calls. The channel runs on the workspace's NuTalk call line and counts toward the plan's channels. Only `type: \"voice\"` is created here: WhatsApp, Instagram, Messenger, Apple Messages, the website widget and Zoom are connected in the dashboard, and numbers are bought or verified there.\n\nRequires the `channels:write` scope.\n\nThis puts an agent in front of real callers: calls to the number reach it as soon as the channel exists. Confirm with the user first.\n\nThe channel is created whole or not at all: when the number can't be put on it, the new channel is deleted again.\n\nCreating never waits for approval, except with `confirm_move_from_channel_id`: taking another channel's incoming calls is a change, so it waits for an owner's or admin's approval when the key's changes do, as the same move through `PUT /v1/channels/{id}/incoming-calls` does.\n\nThe change runs the dashboard's channel rules as the member who created the key: the key needs `channels:write` and that member a role that changes channels (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#createChannel"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"`voice`: a phone channel.","type":"string","enum":["voice"]},"agent_id":{"description":"The agent that answers and calls on the number: one of the workspace's, not archived. An agent that isn't deployed yet answers once it is (`POST /v1/agents/:id/deploy`).","type":"string","format":"uuid"},"phone_number_id":{"description":"The number, by its id (a channel's `phone_number_id`). Send this or `phone_number`.","type":"string","format":"uuid"},"phone_number":{"description":"The number in E.164 (`+528112345678`): one the workspace bought or verified in NuTalk. Send this or `phone_number_id`.","type":"string","maxLength":16},"name":{"description":"The channel's name. Default: the agent's name and \" - Voice\".","type":"string","minLength":1,"maxLength":200},"language":{"description":"The language the channel speaks. Default: the voice's.","type":"string","enum":["es-MX","en-US"]},"voice_id":{"description":"A voice of `GET /v1/voices`. Default: the agent's own voice when it is on the list, else Regina.","type":"string","maxLength":64},"receive_calls":{"description":"Whether the number's incoming calls ring the new channel. Default: yes for a number bought through NuTalk (unless its calls ring in another workspace); a verified number only places calls.","type":"boolean"},"confirm_move_from_channel_id":{"description":"When the number's calls ring another of your channels: that channel's id (the 409 `inbound_number_in_use` gives it), confirming they move here. That channel stops receiving them.","type":"string","format":"uuid"}},"required":["type","agent_id"]},"example":{"type":"voice","agent_id":"550e8400-e29b-41d4-a716-446655440000","phone_number":"+525512345678","name":"Support line","voice_id":"9Godp7dNohUvXk6qp0gS"}}}},"responses":{"201":{"description":"The channel, as `GET /v1/channels/:id` answers it.","content":{"application/json":{"example":{"success":true,"data":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support line","type":"voice","status":"connected","health":"healthy","phone_number":"+525512345678","phone_number_id":"2f6d8b0a-4c1e-4f3a-9b5d-7e9f1a3c5b72","inbound_enabled":true,"voicemail_enabled":false,"voicemail_message":null,"block_anonymous_callers":false,"auto_evaluate_enabled":true,"evaluation_sample_rate":1,"retry_enabled":false,"retry_config":{},"callbacks_enabled":false,"messages_monthly":0,"active_conversations":0,"connected_at":"2026-10-02T02:23:10.004+00:00","created_at":"2026-10-02T02:23:10.004+00:00","updated_at":"2026-10-02T02:23:10.004+00:00","status_detail":null,"language":"es-MX","voice":{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center"},"receives_calls":true,"incoming_calls_number":"+525512345678","scheduling_mode":null,"agents":[{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"}]}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `type` is not `voice` (other channels are connected in the dashboard), or the voice is not on the list (`details.reason` `VOICE_NOT_OFFERED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't change channels or has left the workspace, or the key is a reseller key. `forbidden`: The workspace has all the channels its plan allows (`details.reason` `CHANNEL_LIMIT_REACHED`: delete one, or change the plan in Settings → Billing) or no phone channels (`CHANNEL_TYPE_NOT_ENABLED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace. `not_found`: No phone number with this id (or this number) in your workspace (numbers of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent is archived (`details.reason` `AGENT_ARCHIVED`), or NuTalk's voice servers have no free line for the workspace right now (`VOICE_CAPACITY_UNAVAILABLE`: try again later). `conflict`: The number can't be a channel's number now (`details.reason`): its purchase failed (`CALLER_ID_UNAVAILABLE`), it waits for the carrier's approval (`NUMBER_NOT_ACTIVE`), its verification isn't finished (`CALLER_ID_NOT_VERIFIED`), it is suspended (`phone_number_unavailable`) or it is an AiBox line (`number_managed_in_dashboard`). `conflict`: The number's incoming calls can't come here as asked (`details.reason`): it rings another of your channels (`inbound_number_in_use`, with `details.channel_id` and `details.channel_name`: confirm the move with `confirm_move_from_channel_id`), it answers on your AI PBX (`number_answers_on_ai_pbx`: remove its line on the AI PBX page first), it rings in another workspace (`inbound_number_in_other_workspace`), a verified number only places calls (`verified_number`), or its calls follow its carrier's settings (`carrier_routes_number`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}":{"get":{"operationId":"getChannel","summary":"Get a channel","description":"Returns one channel with its call settings (voicemail, retries, callbacks, evaluation sampling) and the agents on it.\n\nRequires the `channels:read` scope.\n\nA WhatsApp sync channel reads `status: \"unknown\"` (and `health: \"warning\"`) while its sync service is not reporting, because its last known state cannot be trusted then; `status_detail` gives the `reason` (`bridge_offline` or `no_heartbeat`) and `since`, the last time it reported. It is `null` for every other channel.\n\nA phone (`voice`) channel also has `language`, the language it speaks, `voice`, the voice it speaks with (`id` and `name`; change it with `PUT /v1/channels/:id/voice`), `receives_calls`, whether calls to its number ring it now (`inbound_enabled` is only the channel's own switch), and `incoming_calls_number`, the number whose calls ring it now: its own, or an earlier number still routed to it (`null` when none does). They are `null` on other channels, and the last two on a Zoom Phone channel, whose lines are set on its Zoom page. `phone_number_id` is its number's id, and `scheduling_mode` the scheduling system set on any channel (`null` when none was set; `PUT /v1/channels/:id/scheduling`).\n\nPhone (voice) channels are created with `POST /v1/channels` on a number the workspace has. Connecting WhatsApp, Instagram, Messenger, Apple Messages or Zoom and installing the website widget need a person (a Meta or Zoom login, a device), so those channels are created in the dashboard.","tags":["Channels"],"x-required-scope":"channels:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#getChannel"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The channel.","content":{"application/json":{"example":{"success":true,"data":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support Agent - Voice","type":"voice","status":"connected","health":"healthy","phone_number":"+525512345678","phone_number_id":"2f6d8b0a-4c1e-4f3a-9b5d-7e9f1a3c5b72","inbound_enabled":true,"voicemail_enabled":false,"messages_monthly":0,"active_conversations":0,"connected_at":"2026-10-02T02:23:10.004+00:00","created_at":"2026-10-02T02:23:10.004+00:00","updated_at":"2026-10-02T02:23:10.004+00:00","status_detail":null,"language":"es-MX","voice":{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center"},"receives_calls":true,"incoming_calls_number":"+525512345678","scheduling_mode":null,"voicemail_message":null,"block_anonymous_callers":false,"auto_evaluate_enabled":true,"evaluation_sample_rate":1,"retry_enabled":false,"retry_config":{},"callbacks_enabled":false,"agents":[{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteChannel","summary":"Delete a channel","description":"Deletes a channel of any type, as the dashboard does. A phone channel's number stops ringing it and becomes free for another channel, and campaigns that called from it lose their number. A WhatsApp, Instagram or Messenger channel stops receiving messages: NuTalk unsubscribes from its account's webhooks at Meta. A website widget or Live Room stops answering visitors, and an Apple Messages channel's Mac is unlinked. Its conversations and their messages are permanently deleted: `delete_message_history: true` (in the body or the query string) confirms that. The Zoom channel is deleted on its own page in the dashboard, which retires its lines.\n\nRequires the `channels:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nNothing brings the channel or its history back: confirm with the user first.\n\nThe change runs the dashboard's channel rules as the member who created the key: the key needs `channels:write` and that member a role that changes channels (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#deleteChannel"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"delete_message_history","in":"query","required":false,"description":"`true` confirms the delete (or send it in the body).","schema":{"type":"string","enum":["true","false"]}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"delete_message_history":{"description":"`true` (here or in the query string, and required in one of them): confirms that the channel's conversations and messages are permanently deleted with it.","type":"boolean"}}},"example":{"delete_message_history":true}}}},"responses":{"200":{"description":"The channel is deleted, with the conversations and messages that went with it.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","deleted":true,"message_history_deleted":true,"conversations_deleted":12,"messages_deleted":148}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteChannel"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `delete_message_history` is not `true` (`details.reason` `confirmation_required`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't change channels or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The channel is the Zoom channel, managed on its own page in the dashboard (`details.reason` `ZOOM_CHANNEL_MANAGED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateChannel","summary":"Update a channel","description":"Changes the settings you send: the channel's name, its automatic evaluation (on or off, the share of finished conversations evaluated, and on messaging channels how long a silence ends a conversation) and, on a phone channel, whether it refuses callers with a hidden or unknown number. Numbers, connections and the widget's look are set in the dashboard.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's channel rules as the member who created the key: the key needs `channels:write` and that member a role that changes channels (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#updateChannel"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The channel's name.","type":"string","minLength":1,"maxLength":200},"auto_evaluate_enabled":{"description":"Evaluate the channel's finished conversations and calls against the agent's goals and competencies.","type":"boolean"},"evaluation_sample_rate":{"description":"The share of finished conversations evaluated (1: every one).","type":"number","minimum":0,"maximum":1},"conversation_inactivity_timeout_minutes":{"description":"Messaging channels only: minutes of silence after which a conversation counts as finished (and is evaluated).","type":"integer","minimum":5,"maximum":43200},"block_anonymous_callers":{"description":"Phone (`voice`) channels only: refuse calls with a hidden, restricted or unknown caller ID.","type":"boolean"}}},"example":{"name":"Support line","evaluation_sample_rate":0.5,"block_anonymous_callers":true}}}},"responses":{"200":{"description":"The channel, as `GET /v1/channels/:id` answers it.","content":{"application/json":{"example":{"success":true,"data":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support line","type":"voice","status":"connected","health":"healthy","phone_number":"+525512345678","phone_number_id":"2f6d8b0a-4c1e-4f3a-9b5d-7e9f1a3c5b72","inbound_enabled":true,"voicemail_enabled":false,"voicemail_message":null,"block_anonymous_callers":true,"auto_evaluate_enabled":true,"evaluation_sample_rate":0.5,"retry_enabled":false,"retry_config":{},"callbacks_enabled":false,"messages_monthly":0,"active_conversations":0,"connected_at":"2026-10-02T02:23:10.004+00:00","created_at":"2026-10-02T02:23:10.004+00:00","updated_at":"2026-10-12T18:20:05.117+00:00","status_detail":null,"language":"es-MX","voice":{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center"},"receives_calls":true,"incoming_calls_number":"+525512345678","scheduling_mode":null,"agents":[{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Support Agent"}]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateChannel"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent, or a setting doesn't apply to the channel's type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't change channels or has left the workspace, or the key is a reseller key. `forbidden`: A Zoom channel is renamed by a key whose creator is not an owner or admin (`details.reason` `ZOOM_MANAGERS_ONLY`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/phone-number":{"put":{"operationId":"setChannelPhoneNumber","summary":"Change a channel's number","description":"Gives a phone channel another of the workspace's numbers, as the channel page's number picker does: the agent calls from it from now on. Where the number's incoming calls ring changes only when you say so: `receive_calls: true` routes them to this channel, `false` takes them off it, and without it they stay where they ring (an earlier number still routed here keeps ringing the channel).\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nWith `receive_calls: true`, callers of the number reach the agent at once: confirm with the user first.\n\nThe change runs the dashboard's channel rules as the member who created the key: the key needs `channels:write` and that member a role that changes channels (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#setChannelPhoneNumber"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_number_id":{"description":"The new number, by its id (a channel's `phone_number_id`). Send this or `phone_number`.","type":"string","format":"uuid"},"phone_number":{"description":"The new number in E.164 (`+528112345678`): one the workspace bought or verified in NuTalk. Send this or `phone_number_id`.","type":"string","maxLength":16},"receive_calls":{"description":"`true`: the number's incoming calls ring this channel. `false`: they stop ringing it. Left out: they stay as they are.","type":"boolean"},"confirm_move_from_channel_id":{"description":"When the number's calls ring another of your channels: that channel's id (the 409 `inbound_number_in_use` gives it), confirming they move here. That channel stops receiving them.","type":"string","format":"uuid"}}},"example":{"phone_number":"+525512345678","receive_calls":true}}}},"responses":{"200":{"description":"The channel's number now, whether calls to it ring the channel, and `incoming_calls_number`, the number whose calls ring it (an earlier number while its calls weren't moved; `null` when none does). `moved_from_channel_id` names the channel its calls moved from, if they did.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","phone_number_id":"2f6d8b0a-4c1e-4f3a-9b5d-7e9f1a3c5b72","phone_number":"+525512345678","receives_calls":true,"incoming_calls_number":"+525512345678","moved_from_channel_id":null}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setChannelPhoneNumber"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The channel is not a phone (`voice`) channel (`details.reason` `channel_not_voice`): messaging channels are set up in the dashboard.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't change channels or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same). `not_found`: No phone number with this id (or this number) in your workspace (numbers of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The number can't be a channel's number now (`details.reason`): its purchase failed (`CALLER_ID_UNAVAILABLE`), it waits for the carrier's approval (`NUMBER_NOT_ACTIVE`), its verification isn't finished (`CALLER_ID_NOT_VERIFIED`), it is suspended (`phone_number_unavailable`) or it is an AiBox line (`number_managed_in_dashboard`). `conflict`: The number's incoming calls can't come here as asked (`details.reason`): it rings another of your channels (`inbound_number_in_use`, with `details.channel_id` and `details.channel_name`: confirm the move with `confirm_move_from_channel_id`), it answers on your AI PBX (`number_answers_on_ai_pbx`: remove its line on the AI PBX page first), it rings in another workspace (`inbound_number_in_other_workspace`), a verified number only places calls (`verified_number`), or its calls follow its carrier's settings (`carrier_routes_number`). `conflict`: The channel answers on its AiBox (`details.reason` `channel_has_voice_device`: its line changes in the dashboard), or it already receives calls on another number (`channel_has_inbound_number`: turn its incoming calls off first). `receive_calls: false` with a number NuTalk doesn't route, a Twilio or verified one (`receive_calls_needs_switch`): change the number without it, then stop the calls with `PUT /v1/channels/{id}/incoming-calls`. Nothing changed. `conflict`: The channel is the Zoom channel, managed on its own page in the dashboard (`details.reason` `ZOOM_CHANNEL_MANAGED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/incoming-calls":{"put":{"operationId":"setChannelIncomingCalls","summary":"Turn a channel's incoming calls on or off","description":"The channel page's incoming-calls switch: `receive_calls: true` makes calls to the channel's number ring it, and its agent answers them; `false` stops them (the call is rejected: callers hear busy). A number whose calls ring another of your channels moves here only with `confirm_move_from_channel_id`, and that channel stops receiving them.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nTurning calls on puts the agent in front of real callers: confirm with the user first. Turning them off is a stop, so it never waits for approval.\n\nThe change runs the dashboard's channel rules as the member who created the key: the key needs `channels:write` and that member a role that changes channels (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#setChannelIncomingCalls"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"receive_calls":{"description":"Whether calls to the channel's number ring it.","type":"boolean"},"confirm_move_from_channel_id":{"description":"When the number's calls ring another of your channels: that channel's id (the 409 `inbound_number_in_use` gives it), confirming they move here. That channel stops receiving them.","type":"string","format":"uuid"}},"required":["receive_calls"]},"example":{"receive_calls":true}}}},"responses":{"200":{"description":"Whether calls ring the channel now. `changed` is false when they already did (or didn't); `moved_from_channel_id` and `moved_from_channel_name` name the channel the calls moved from.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","receives_calls":true,"changed":true,"phone_number":"+525512345678","moved_from_channel_id":null,"moved_from_channel_name":null}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setChannelIncomingCalls"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The channel is not a phone (`voice`) channel (`details.reason` `channel_not_voice`): messaging channels are set up in the dashboard.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't change channels or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The channel has no number (`details.reason` `no_phone_number`: give it one with `PUT /v1/channels/:id/phone-number`), or its number can't take calls now (`phone_number_not_active`, `phone_number_unavailable`). `conflict`: The number's incoming calls can't come here as asked (`details.reason`): it rings another of your channels (`inbound_number_in_use`, with `details.channel_id` and `details.channel_name`: confirm the move with `confirm_move_from_channel_id`), it answers on your AI PBX (`number_answers_on_ai_pbx`: remove its line on the AI PBX page first), it rings in another workspace (`inbound_number_in_other_workspace`), a verified number only places calls (`verified_number`), or its calls follow its carrier's settings (`carrier_routes_number`). `conflict`: The channel is the Zoom channel, managed on its own page in the dashboard (`details.reason` `ZOOM_CHANNEL_MANAGED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/voice":{"put":{"operationId":"setChannelVoice","summary":"Change a channel's voice","description":"Changes how a phone channel speaks: its voice (one of `GET /v1/voices`), its language and the voice's settings, as the channel's Voice editor saves them. Everything else stays as the channel has it. A newly picked voice starts with the settings its demo was recorded with (speed 1, stability 0.5, similarity 0.75), unless `voice_settings` says otherwise; a channel keeps a voice it already has even when it left the list.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's channel rules as the member who created the key: the key needs `channels:write` and that member a role that changes channels (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#setChannelVoice"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"voice_id":{"description":"A voice of `GET /v1/voices`.","type":"string","maxLength":64},"language":{"description":"The language the channel speaks (and understands): its speech recognition follows it. Unchanged when left out, even with another voice.","type":"string","enum":["es-MX","en-US"]},"voice_settings":{"description":"The voice's settings; the ones left out keep their value.","type":"object","properties":{"speed":{"description":"How fast it speaks (1: normal).","type":"number","minimum":0.7,"maximum":1.3},"stability":{"description":"Higher sounds steadier, lower more expressive.","type":"number","minimum":0,"maximum":1},"similarity":{"description":"How closely it keeps to the original voice.","type":"number","minimum":0,"maximum":1}}}}},"example":{"voice_id":"9Godp7dNohUvXk6qp0gS","voice_settings":{"speed":1.1}}}}},"responses":{"200":{"description":"How the channel speaks now.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","voice":{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center"},"language":"es-MX","voice_settings":{"speed":1.1,"stability":0.5,"similarity":0.75}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setChannelVoice"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: Nothing to change was sent, or the voice is not on the list (`details.reason` `VOICE_NOT_OFFERED`). `validation_error`: The channel is not a phone (`voice`) channel (`details.reason` `channel_not_voice`): messaging channels are set up in the dashboard.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"`validation_error`: The workspace's plan has no call line for the channel yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`forbidden`: The member who created the key has a role that can't change channels or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: NuTalk's voice servers have no free line for the workspace (`details.reason` `VOICE_CAPACITY_UNAVAILABLE`), or the channel's call settings need to be saved once in the dashboard first (`voice_settings_in_dashboard`). `conflict`: The channel is the Zoom channel, managed on its own page in the dashboard (`details.reason` `ZOOM_CHANNEL_MANAGED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/voices":{"get":{"operationId":"listVoices","summary":"List the voices","description":"The voices a phone channel can speak with: the same list every voice picker in the dashboard offers, featured ones first, with a recording of each. Regina (`default`: true) is the one a new channel starts with when neither the request nor its agent picks another.\n\nRequires the `channels:read` scope.","tags":["Channels"],"x-required-scope":"channels:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#listVoices"},"parameters":[{"name":"language","in":"query","required":false,"description":"Only the voices that speak this language.","schema":{"type":"string","enum":["es-MX","en-US"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The voices (the whole list: it isn't paginated).","content":{"application/json":{"example":{"success":true,"data":[{"id":"9Godp7dNohUvXk6qp0gS","name":"Regina - Contact Center","description":"Young, friendly and clear. Built for phone support and sales.","language":"es-MX","gender":"female","age":"young","preview_url":"https://www.nutalk.ai/voice-previews/9Godp7dNohUvXk6qp0gS.mp3","default":true}]}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/agent":{"put":{"operationId":"setChannelAgent","summary":"Put an agent on a channel","description":"Makes the agent the one that answers on the channel, with its deployed version, as the dashboard's channel picker does: any other agent on the channel is taken off it (a website widget's chat and calls both go to this agent), and later deploys of the agent move the channel to the new version. The agent must be deployed and not archived.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nCustomers reach the agent at once: on a messaging channel, whether the AI answers them also depends on the channel's AI mode (`PUT /v1/channels/:id/ai-mode`).\n\nThe change runs the dashboard's channel rules as the member who created the key: the key needs `channels:write` and that member a role that changes channels (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#setChannelAgent"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"description":"The agent to put on the channel.","type":"string","format":"uuid"}},"required":["agent_id"]},"example":{"agent_id":"550e8400-e29b-41d4-a716-446655440000"}}}},"responses":{"200":{"description":"The agent answers on the channel with this version.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setChannelAgent"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't change channels or has left the workspace, or the key is a reseller key. `forbidden`: The channel is a Zoom channel and the key's creator is not an owner or admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same). `not_found`: No agent with this id in your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent is archived (`details.reason` `AGENT_ARCHIVED`) or has no deployed version, or the website widget's settings are missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"removeChannelAgent","summary":"Take the agent off a channel","description":"Takes an agent off the channel, as the agent page's unlink does: everything that makes it answer there goes (the channel's binding to its version, its widget roles, the assignment), and nothing of another agent's. The channel stays connected, with its settings, and has no agent until one is put on it. Name the agent with `agent_id` (in the body or the query string); without it, the channel's only agent is taken off.\n\nRequires the `channels:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's channel rules as the member who created the key: the key needs `channels:write` and that member a role that changes channels (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#removeChannelAgent"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"agent_id","in":"query","required":false,"description":"The agent to take off (or send it in the body).","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_id":{"description":"The agent to take off. Default: the channel's only agent.","type":"string","format":"uuid"}}},"example":{"agent_id":"550e8400-e29b-41d4-a716-446655440000"}}}},"responses":{"200":{"description":"`unlinked` is false when the agent (or, without `agent_id`, any agent) didn't answer on the channel: nothing changed.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","agent_id":"550e8400-e29b-41d4-a716-446655440000","unlinked":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"removeChannelAgent"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't change channels or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same). `not_found`: No agent with this id in your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The channel has more than one agent and `agent_id` is missing (`details.agent_ids`), or it is a Zoom channel, which always has an agent (`details.reason` `ZOOM_CHANNEL_NEEDS_AGENT`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/ai-mode":{"put":{"operationId":"setChannelAiMode","summary":"Switch who answers a channel","description":"Switches who answers a messaging channel's chats (WhatsApp, Instagram, Messenger, website widget, Apple Messages): `off`, the team answers (the AI only answers the chats a person let it answer); `testing`, the AI answers only the channel's test numbers (phone-number channels); `live`, the AI answers new conversations, and with `live_scope: all` existing chats too from their next message; `paused`, the emergency stop; `resume`, the AI back on exactly as it was before the pause. It goes through the dashboard's one switch, under the channel's lock, and is recorded in the channel's activity.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThis puts the AI in front of real customers (or takes it away): switch with the same care as in the dashboard. Only owners and admins switch it: the key's creator must be one of them.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#setChannelAiMode"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"description":"Who answers.","type":"string","enum":["off","testing","live","paused","resume"]},"live_scope":{"description":"With `live` only: `new` (only conversations that start from now; the default when the channel was off or testing) or `all` (existing chats too, from the customer's next message; nothing older is answered).","type":"string","enum":["new","all"]}},"required":["mode"]},"example":{"mode":"live","live_scope":"new"}}}},"responses":{"200":{"description":"Who answers now. `changed` is false when the channel already was in that mode; `open_chats` counts the open chats the AI and the team answer.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"8a44ec22-04c7-4352-b90a-0a35d5aee0ec","mode":"live","live_scope":"new","changed":true,"resumed":false,"paused_from":null,"testing_available":false,"test_numbers":0,"open_chats":{"ai":0,"team":14},"mode_changed_at":"2026-10-12T18:25:40.902+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setChannelAiMode"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `testing` on a channel whose chats are not phone numbers (`details.reason` `TESTING_NEEDS_PHONE_NUMBERS`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key is not an owner or admin, has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No messaging channel with this id in your workspace (a phone channel has no AI mode).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: `resume` on a channel that is not paused (`NOT_PAUSED`) or has no state to go back to (`RESUME_NEEDS_MODE`), or `live` with `new` on a channel live for every chat (`LIVE_CANNOT_NARROW`: turn it off first).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/scheduling":{"put":{"operationId":"setChannelScheduling","summary":"Set a channel's scheduling system","description":"Sets the only scheduling system the agent on the channel may book with: `calcom`, `business` (Business appointments), `clinic`, `restaurant`, or `none` to turn booking off on this channel. A system can be chosen only when the workspace has it (its module, or a connected Cal.com), and an agent books one way only: when its booking method was chosen, the channel takes that method or `none`.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's channel rules as the member who created the key: the key needs `channels:write` and that member a role that changes channels (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#setChannelScheduling"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"description":"The scheduling system.","type":"string","enum":["none","calcom","business","clinic","restaurant"]}},"required":["mode"]},"example":{"mode":"clinic"}}}},"responses":{"200":{"description":"The channel's scheduling system, and the one it had (`null`: none was set). With `business`, a `warning` with code `agent_cannot_book_business` says the channel's agent has no meeting type it can book: the change is saved, customers who ask for a meeting are told the team will confirm it, and the workspace's owners, admins and builders are notified. Choose the agent's meeting types to fix it.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","mode":"clinic","previous_mode":null}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setChannelScheduling"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The workspace doesn't have this system (`details.reason`: `module_missing`, `calcom_not_connected`, `ai_channels_not_wired`), or the agent books with another one (`scheduling_mode_not_agent_system`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't change channels or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The change would give the channel's agent a second way to book (`details.reason` `booking_method_conflict`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/web-widget":{"post":{"operationId":"createWebWidget","summary":"Create a website widget","description":"Creates a website widget channel, as Channels → Website widget does: an agent that chats and one that takes calls (each with a deployed version; the call agent defaults to the chat agent), the modes the workspace has, the websites it runs on, its greeting and its look. The answer carries `embed_key`, which the website's snippet uses; it is public by design.\n\nRequires the `channels:write` scope.\n\nA change runs the dashboard's own rules as the member who created the key, keeping every setting the request leaves out as saved. A key whose creator has left the workspace, and a reseller key, are refused.\n\nOnce its snippet is on a website, the agent answers its visitors.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#createWebWidget"},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The channel's name (default: the agent's name and \"Website Widget\").","type":"string","maxLength":120},"chat_agent_id":{"description":"The agent that chats; it needs a deployed version.","type":"string","format":"uuid"},"voice_agent_id":{"description":"The agent that takes calls (default: the chat agent).","type":"string","format":"uuid"},"modes":{"description":"What visitors can do: `chat`, `voice` (calls) or both. A mode must be one the workspace has.","type":"array","minItems":1,"maxItems":2,"items":{"description":"A mode.","type":"string","enum":["chat","voice"]}},"default_mode":{"description":"The mode it opens in; one of its modes.","type":"string","enum":["chat","voice"]},"allowed_domains":{"description":"The websites it runs on; `[]` lets it run on any website.","type":"array","maxItems":50,"items":{"description":"A hostname such as example.com.","type":"string"}},"greeting":{"description":"What it says first.","type":"string","maxLength":500},"consent_text":{"description":"A consent notice shown before visitors start.","type":["string","null"],"maxLength":2000},"theme":{"description":"Its look; only the settings sent change.","type":"object","properties":{"accent":{"description":"A hex color such as `#14b8a6`.","type":"string"},"position":{"description":"Where the launcher sits.","type":"string","enum":["bottom-right","bottom-left"]},"assistant_name":{"description":"The name shown on the widget.","type":["string","null"],"maxLength":60},"locale":{"description":"Its language; `auto` follows the visitor's browser.","type":"string","enum":["auto","en","es"]}}}}},"example":{"name":"Website","chat_agent_id":"550e8400-e29b-41d4-a716-446655440000","modes":["chat","voice"],"allowed_domains":["example.com","www.example.com"],"theme":{"accent":"#14b8a6","assistant_name":"Sofía"}}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"8a44ec22-04c7-4352-b90a-0a35d5aee0ec","name":"Website","embed_key":"ww_00000000aaaa4aaa8aaa000000000001","chat_agent_id":"550e8400-e29b-41d4-a716-446655440000","voice_agent_id":"550e8400-e29b-41d4-a716-446655440000","modes":["chat","voice"],"default_mode":"chat","allowed_domains":["example.com","www.example.com"],"greeting":"Hi! Would you like to chat or start a call?","consent_text":null,"theme":{"accent":"#14b8a6","position":"bottom-right","assistant_name":"Sofía","locale":"auto"},"capture_fields":{"name":true,"email":true,"phone":false,"require_before_start":false}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: A mode the workspace doesn't have (`details.reason` `WIDGET_MODE_NOT_INCLUDED`: it is added in Billing), the plan's channel limit, or the member who created the key can't create channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: An agent isn't the workspace's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/web-widget":{"get":{"operationId":"getWebWidget","summary":"Get a website widget's settings","description":"The website widget's agents, modes, the websites it runs on, greeting, consent notice, look, the visitor details it asks for and its embed key.\n\nRequires the `channels:read` scope.","tags":["Channels"],"x-required-scope":"channels:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#getWebWidget"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The widget.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"8a44ec22-04c7-4352-b90a-0a35d5aee0ec","name":"Website","embed_key":"ww_00000000aaaa4aaa8aaa000000000001","chat_agent_id":"550e8400-e29b-41d4-a716-446655440000","voice_agent_id":"550e8400-e29b-41d4-a716-446655440000","modes":["chat","voice"],"default_mode":"chat","allowed_domains":["example.com","www.example.com"],"greeting":"Hi! Would you like to chat or start a call?","consent_text":null,"theme":{"accent":"#14b8a6","position":"bottom-right","assistant_name":"Sofía","locale":"auto"},"capture_fields":{"name":true,"email":true,"phone":false,"require_before_start":false}}}}}},"400":{"description":"`validation_error`: The channel isn't a website widget (`details.reason` `not_web_widget`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateWebWidget","summary":"Change a website widget","description":"Changes the settings sent; the rest stay as saved (the websites it runs on are never cleared by a request that leaves them out). A mode switched on must be one the workspace has; one already on stays. `capture_fields` sets the visitor details it asks for and whether it asks before they start.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key, keeping every setting the request leaves out as saved. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#updateWebWidget"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"chat_agent_id":{"description":"The agent that chats; it needs a deployed version.","type":"string","format":"uuid"},"voice_agent_id":{"description":"The agent that takes calls (default: the chat agent).","type":"string","format":"uuid"},"modes":{"description":"What visitors can do: `chat`, `voice` (calls) or both. A mode must be one the workspace has.","type":"array","minItems":1,"maxItems":2,"items":{"description":"A mode.","type":"string","enum":["chat","voice"]}},"default_mode":{"description":"The mode it opens in; one of its modes.","type":"string","enum":["chat","voice"]},"allowed_domains":{"description":"The websites it runs on; `[]` lets it run on any website.","type":"array","maxItems":50,"items":{"description":"A hostname such as example.com.","type":"string"}},"greeting":{"description":"What it says first.","type":"string","maxLength":500},"consent_text":{"description":"A consent notice shown before visitors start.","type":["string","null"],"maxLength":2000},"theme":{"description":"Its look; only the settings sent change.","type":"object","properties":{"accent":{"description":"A hex color such as `#14b8a6`.","type":"string"},"position":{"description":"Where the launcher sits.","type":"string","enum":["bottom-right","bottom-left"]},"assistant_name":{"description":"The name shown on the widget.","type":["string","null"],"maxLength":60},"locale":{"description":"Its language; `auto` follows the visitor's browser.","type":"string","enum":["auto","en","es"]}}},"capture_fields":{"description":"The visitor details it asks for.","type":"object","properties":{"name":{"description":"Ask the visitor's name.","type":"boolean"},"email":{"description":"Ask their email.","type":"boolean"},"phone":{"description":"Ask their phone.","type":"boolean"},"require_before_start":{"description":"Ask before the chat or call starts.","type":"boolean"}}}}},"example":{"allowed_domains":["example.com","shop.example.com"],"greeting":"Hi! How can I help?"}}}},"responses":{"200":{"description":"The widget as saved.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"8a44ec22-04c7-4352-b90a-0a35d5aee0ec","name":"Website","embed_key":"ww_00000000aaaa4aaa8aaa000000000001","chat_agent_id":"550e8400-e29b-41d4-a716-446655440000","voice_agent_id":"550e8400-e29b-41d4-a716-446655440000","modes":["chat","voice"],"default_mode":"chat","allowed_domains":["example.com","www.example.com"],"greeting":"Hi! Would you like to chat or start a call?","consent_text":null,"theme":{"accent":"#14b8a6","position":"bottom-right","assistant_name":"Sofía","locale":"auto"},"capture_fields":{"name":true,"email":true,"phone":false,"require_before_start":false}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateWebWidget"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The channel isn't a website widget (`details.reason` `not_web_widget`), or an agent has no deployed version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: A mode switched on the workspace doesn't have (`WIDGET_MODE_NOT_INCLUDED`), or the member who created the key can't change channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/messaging-settings":{"get":{"operationId":"getChannelMessagingSettings","summary":"Get a messaging channel's settings","description":"For a WhatsApp, Instagram, Messenger or Apple Messages channel: who gets its chats when the AI hands them over (`default_operator_id`, a teammate), WhatsApp's human-like timing and grouping of quick messages in a row, and whether a team message from outside NuTalk holds the chat (`team_first_message_holds`: the first message on Apple Messages, Instagram and Messenger, a reply from the phone or WhatsApp Web on a WhatsApp QR line). A setting the channel doesn't take is null. The AI mode is GET /v1/channels/{id}.\n\nRequires the `channels:read` scope.","tags":["Channels"],"x-required-scope":"channels:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#getChannelMessagingSettings"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The settings.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","type":"whatsapp","default_operator_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","humanlike_timing":true,"turn_grouping":true,"team_first_message_holds":null}}}}},"400":{"description":"`validation_error`: The channel isn't a messaging channel (`details.reason` `not_messaging_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateChannelMessagingSettings","summary":"Change a messaging channel's settings","description":"Changes the settings sent; the rest stay as saved, and never the channel's transport or its AI mode (PUT /v1/channels/{id}/ai-mode). A setting the channel doesn't take is refused. Setting an Instagram or Messenger channel's default operator also assigns them every chat nobody has, so it needs `conversations:write` too.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key, keeping every setting the request leaves out as saved. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#updateChannelMessagingSettings"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"default_operator_id":{"description":"A teammate who gets the chats the AI hands over: an owner, admin, operator, clinic manager, clinic messages or receptionist (who take chats in the inbox); null for none. WhatsApp, Instagram and Messenger.","type":["string","null"],"format":"uuid"},"humanlike_timing":{"description":"WhatsApp: the AI answers with a person's pace.","type":"boolean"},"turn_grouping":{"description":"WhatsApp: quick messages in a row get one answer.","type":"boolean"},"team_first_message_holds":{"description":"Apple Messages, Instagram, Messenger: a chat the team writes first opens on hold, with the AI waiting. WhatsApp QR lines: a person's reply from the line's phone or WhatsApp Web stops the agent in that chat until a teammate hands it back. Off on WhatsApp QR lines until set; nothing hands those chats back by itself, and turning it off leaves them held. Reminders, follow-ups and campaign messages in those chats follow the agent's automations (`appointment_reminders.remind_held_chats`, `follow_ups.follow_up_held_chats`, `campaign_messages.send_to_held_chats`). A WhatsApp API or SMS line refuses it.","type":"boolean"}}},"example":{"default_operator_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","humanlike_timing":false}}}},"responses":{"200":{"description":"The settings as saved.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","type":"whatsapp","default_operator_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","humanlike_timing":false,"turn_grouping":true,"team_first_message_holds":null}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateChannelMessagingSettings"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The default operator isn't a teammate of the workspace who takes chats (a viewer or a builder doesn't), the channel isn't a messaging channel, or it doesn't take a setting sent (`details.errors` names it; on WhatsApp, `team_first_message_holds` is for QR lines only).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner or admin (the role the dashboard asks for this setting), has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/ivr-navigation":{"get":{"operationId":"getChannelIvrNavigation","summary":"Get a phone channel's menu navigation","description":"Whether the agent presses keys through phone menus on the channel's outbound calls, what it aims for there (`goal`, e.g. reach the billing department) and the keys it already knows (`known_sequence`).\n\nRequires the `channels:read` scope.","tags":["Channels"],"x-required-scope":"channels:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#getChannelIvrNavigation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The menu navigation.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","enabled":true,"goal":"Reach the purchasing department","known_sequence":["2","1"]}}}}},"400":{"description":"`validation_error`: The channel isn't a phone channel (`details.reason` `not_voice_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"setChannelIvrNavigation","summary":"Change a phone channel's menu navigation","description":"Changes the settings sent; the rest stay as saved. It applies to the channel's next outbound calls.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key, keeping every setting the request leaves out as saved. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#setChannelIvrNavigation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"The agent presses keys through phone menus.","type":"boolean"},"goal":{"description":"What it aims for in a menu.","type":["string","null"],"maxLength":500},"known_sequence":{"description":"The keys it already knows, menu by menu.","type":"array","maxItems":20,"items":{"description":"Keys to press at one menu: 0-9, * and #.","type":"string"}}}},"example":{"enabled":true,"goal":"Reach the purchasing department","known_sequence":["2","1"]}}}},"responses":{"200":{"description":"The menu navigation as saved.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","enabled":true,"goal":"Reach the purchasing department","known_sequence":["2","1"]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setChannelIvrNavigation"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key can't change channels (owner, admin or builder), has left the workspace, or the key is a reseller key. `forbidden`: The channel is a Zoom Phone line and the member who created the key isn't an owner or admin (`details.reason` `ZOOM_MANAGERS_ONLY`), as for any change to a Zoom channel.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/ai-test-numbers":{"get":{"operationId":"listChannelTestNumbers","summary":"List a channel's test numbers","description":"The numbers the AI answers while the channel is in Testing (AI mode `testing`), with the AI's last reply to each and whether it remembers earlier messages. WhatsApp and Apple Messages channels.\n\nRequires the `channels:read` scope.","tags":["Channels"],"x-required-scope":"channels:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#listChannelTestNumbers"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The test numbers.","content":{"application/json":{"example":{"success":true,"data":[{"id":"3d5e7f9a-1b2c-4d4e-8f6a-7b8c9d0e1f2a","phone_number":"+525512345678","created_at":"2026-10-20T16:00:00.000Z","last_ai_reply_at":"2026-10-26T18:20:00.000Z","last_ai_reply_status":"sent","remembers":true,"last_reset_at":null}]}}}},"400":{"description":"`validation_error`: The channel isn't a WhatsApp or Apple Messages channel (`details.reason` `not_testing_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"addChannelTestNumber","summary":"Add a test number","description":"Adds a number the AI answers while the channel is in Testing, from the next message it sends; that phone's existing chat is answered too. At most 10 per channel. Answers the channel's test numbers. Only add your team's own phones.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key, keeping every setting the request leaves out as saved. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#addChannelTestNumber"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_number":{"description":"The number in E.164, with its country code.","type":"string"}},"required":["phone_number"]},"example":{"phone_number":"+525512345678"}}}},"responses":{"201":{"description":"Added.","content":{"application/json":{"example":{"success":true,"data":[{"id":"3d5e7f9a-1b2c-4d4e-8f6a-7b8c9d0e1f2a","phone_number":"+525512345678","created_at":"2026-10-20T16:00:00.000Z","last_ai_reply_at":"2026-10-26T18:20:00.000Z","last_ai_reply_status":"sent","remembers":true,"last_reset_at":null}]}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"addChannelTestNumber"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The channel isn't a WhatsApp or Apple Messages channel (`details.reason` `not_testing_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner or admin (the role the dashboard asks for this setting), has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The number is already a test number (`TEST_NUMBER_EXISTS`), or the channel has 10 (`TEST_NUMBERS_LIMIT`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/ai-test-numbers/{number_id}":{"delete":{"operationId":"removeChannelTestNumber","summary":"Remove a test number","description":"The AI stops answering the number while the channel is in Testing. A stop, so it never waits for an owner's approval.\n\nRequires the `channels:write` scope.\n\nA change runs the dashboard's own rules as the member who created the key, keeping every setting the request leaves out as saved. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#removeChannelTestNumber"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"number_id","in":"path","required":true,"description":"The test number's id, from GET /v1/channels/{id}/ai-test-numbers.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Removed.","content":{"application/json":{"example":{"success":true,"data":{"id":"3d5e7f9a-1b2c-4d4e-8f6a-7b8c9d0e1f2a","deleted":true}}}}},"400":{"description":"`validation_error`: The channel isn't a WhatsApp or Apple Messages channel (`details.reason` `not_testing_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner or admin (the role the dashboard asks for this setting), has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel or test number with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/ai-test-numbers/{number_id}/start-over":{"post":{"operationId":"startOverChannelTestNumber","summary":"Start the AI over with a test number","description":"The test number's next message is answered as if it were the first: the AI forgets the earlier messages (`messages`), what it remembers about the person (`memory`), or both. Nothing is deleted. It starts the person over, as Start over does in the dashboard: every chat of that phone in the workspace, on any channel, not only this channel's.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key, keeping every setting the request leaves out as saved. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#startOverChannelTestNumber"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"number_id","in":"path","required":true,"description":"The test number's id, from GET /v1/channels/{id}/ai-test-numbers.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"description":"Forget the earlier messages.","type":"boolean"},"memory":{"description":"Forget what it remembers about the person.","type":"boolean"}},"required":["messages","memory"]},"example":{"messages":true,"memory":true}}}},"responses":{"200":{"description":"Started over.","content":{"application/json":{"example":{"success":true,"data":{"id":"3d5e7f9a-1b2c-4d4e-8f6a-7b8c9d0e1f2a","messages":true,"memory":true,"started_over":true}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"startOverChannelTestNumber"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The channel isn't a WhatsApp or Apple Messages channel (`details.reason` `not_testing_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key can't change channels (owner, admin or builder), has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel or test number with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/live-room":{"post":{"operationId":"createLiveRoom","summary":"Create a Live Room","description":"Creates a Live Room, as Channels → Live Room does: a page at its own link where visitors talk with the agent and the page shows what they need as they talk. It starts from a template's questions and screens and the workspace's brand, with the agent that answers (it needs a deployed version) and books the way that agent books. Answers the room as `GET /v1/channels/{id}/live-room` does, with its `url` and its `embed_key` (public by design: a website's snippet carries it).\n\nRequires the `channels:write` scope.\n\nA change runs the dashboard's own rules as the member who created the key, keeping every setting the request leaves out as saved. A key whose creator has left the workspace, and a reseller key, are refused.\n\nIt answers visitors as soon as it is created: share its link, or embed it on a website.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#createLiveRoom"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The channel's name.","type":"string","maxLength":80},"slug":{"description":"Its link: 3 to 48 lowercase letters, numbers and single hyphens, unique across NuTalk.","type":"string"},"template":{"description":"What it starts from: `book_visit` (clinic visits), `book_meeting` (business meetings), `answer_questions` or `blank`. The last two book nothing: the team follows up.","type":"string","enum":["book_visit","book_meeting","answer_questions","blank"]},"agent_id":{"description":"The agent that answers.","type":"string","format":"uuid"}},"required":["name","slug","template","agent_id"]},"example":{"name":"Live Room","slug":"clinica-sol","template":"book_visit","agent_id":"550e8400-e29b-41d4-a716-446655440000"}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"5e1f7a3b-9c2d-4e6f-8a1b-2c3d4e5f6a7b","name":"Live Room","slug":"clinica-sol","url":"https://www.nutalk.ai/r/clinica-sol","embed_key":"lr_clinica-sol","template":"book_visit","agent_id":"550e8400-e29b-41d4-a716-446655440000","accepting_calls":true,"questions":[{"id":"service","kind":"service","label":{"en":"What would you like to book?","es":"¿Qué te gustaría agendar?"},"choices":[],"required":true},{"id":"name","kind":"name","label":{"en":"Your name","es":"Tu nombre"},"choices":[],"required":true}],"on_screen":{"services":true,"places":true,"team":false,"knowledge":false,"prices":true,"knowledge_ids":[]},"look":{"business_name":"Clínica Sol","logo_url":null,"accent":"#0b7476","theme":"dark","background":"soft","font":"nutalk","orb":"sphere","motion":"full","notes":"beside","captions":true,"texts":{"en":{"title":"Talk with Sofía","sub":"Book your visit in a minute.","start":"Start talking"},"es":{"title":"Habla con Sofía","sub":"Agenda tu visita en un minuto.","start":"Empezar a hablar"}}},"allowed_domains":["clinicasol.mx"],"max_call_seconds":600,"consent":{"en":"","es":""}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The link's shape (`details.reason` `SLUG_INVALID`) or a reserved one (`SLUG_RESERVED`), or the agent has no deployed version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The plan doesn't include Live Room or its channel limit is reached, the agent is archived, or the member who created the key can't create channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: NuTalk hasn't switched Live Room on for the workspace, or the agent isn't the workspace's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The link is taken (`details.reason` `SLUG_TAKEN`, with a free one close to it in `details.suggestion`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/live-room":{"get":{"operationId":"getLiveRoom","summary":"Get a Live Room's settings","description":"The Live Room's link and embed key, the agent that answers, whether visitors can talk now, the questions it asks, what it may show on screen, its look, the websites that embed it, the longest call and the recording notice. The agent is changed with `PUT /v1/channels/{id}/agent`.\n\nRequires the `channels:read` scope.","tags":["Channels"],"x-required-scope":"channels:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#getLiveRoom"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The room.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"5e1f7a3b-9c2d-4e6f-8a1b-2c3d4e5f6a7b","name":"Live Room","slug":"clinica-sol","url":"https://www.nutalk.ai/r/clinica-sol","embed_key":"lr_clinica-sol","template":"book_visit","agent_id":"550e8400-e29b-41d4-a716-446655440000","accepting_calls":true,"questions":[{"id":"service","kind":"service","label":{"en":"What would you like to book?","es":"¿Qué te gustaría agendar?"},"choices":[],"required":true},{"id":"name","kind":"name","label":{"en":"Your name","es":"Tu nombre"},"choices":[],"required":true}],"on_screen":{"services":true,"places":true,"team":false,"knowledge":false,"prices":true,"knowledge_ids":[]},"look":{"business_name":"Clínica Sol","logo_url":null,"accent":"#0b7476","theme":"dark","background":"soft","font":"nutalk","orb":"sphere","motion":"full","notes":"beside","captions":true,"texts":{"en":{"title":"Talk with Sofía","sub":"Book your visit in a minute.","start":"Start talking"},"es":{"title":"Habla con Sofía","sub":"Agenda tu visita en un minuto.","start":"Empezar a hablar"}}},"allowed_domains":["clinicasol.mx"],"max_call_seconds":600,"consent":{"en":"","es":""}}}}}},"400":{"description":"`validation_error`: The channel isn't a Live Room (`details.reason` `not_live_room`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateLiveRoom","summary":"Change a Live Room","description":"Changes the settings sent; the rest stay as saved. `questions` replaces the whole list; `on_screen`, `look`, `consent` and the texts change only what they name. `accepting_calls: false` stops new calls at once and never waits for approval.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key, keeping every setting the request leaves out as saved. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#updateLiveRoom"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"accepting_calls":{"description":"Whether visitors can talk now. `false` stops new calls at once; the page then offers a form the team follows up on.","type":"boolean"},"slug":{"description":"Its link: `https://www.nutalk.ai/r/<slug>`, 3 to 48 lowercase letters, numbers and single hyphens, unique across NuTalk. A link that was shared stops working when it changes.","type":"string"},"allowed_domains":{"description":"The websites that may embed it (`[]`: any).","type":"array","maxItems":20,"items":{"description":"A website's address, like clinicasol.mx.","type":"string"}},"max_call_seconds":{"description":"The longest call: 300, 600 (default), 900 or 1200 seconds.","type":"integer","minimum":300,"maximum":1200},"consent":{"description":"The recording notice before the call, per language (`en`, `es`, up to 400 characters); empty shows NuTalk's default. A language left out stays as saved.","type":"object","additionalProperties":true},"questions":{"description":"What the agent asks, in order, replacing the list: each with its own `id` (lowercase letters, digits, `_`), a `kind` (`service` and `location` offer the business's own; `choice` takes 2 to 8 `choices`; `name`, `phone`, `email`, `text`), its `label` per language and `required`. Name, phone and email answers fill the contact; the rest go with the appointment.","type":"array","maxItems":12},"on_screen":{"description":"What the agent may show on screen: `services`, `places`, `team`, `knowledge` and `prices` (off: services show without prices). The ones left out stay as saved; the knowledge items allowed on screen are chosen in the dashboard.","type":"object","additionalProperties":true},"look":{"description":"Its look: `business_name`, `accent` (a hex color), `theme` (`dark`, `light`, `auto`), `background` (`soft`, `plain`, `tint`), `font` (`nutalk`, `inter`, `system`), `orb` (`sphere`, `halo`, `minimal`), `motion` (`full`, `reduced`), `notes` (`beside`, `hidden`), `captions`, and `texts` per language (`title`, `sub`, `start`). What it leaves out stays as saved; the logo is uploaded in the dashboard.","type":"object","additionalProperties":true}}},"example":{"accepting_calls":false,"look":{"texts":{"es":{"title":"Habla con Sofía"}}}}}}},"responses":{"200":{"description":"The room as saved.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"5e1f7a3b-9c2d-4e6f-8a1b-2c3d4e5f6a7b","name":"Live Room","slug":"clinica-sol","url":"https://www.nutalk.ai/r/clinica-sol","embed_key":"lr_clinica-sol","template":"book_visit","agent_id":"550e8400-e29b-41d4-a716-446655440000","accepting_calls":false,"questions":[{"id":"service","kind":"service","label":{"en":"What would you like to book?","es":"¿Qué te gustaría agendar?"},"choices":[],"required":true},{"id":"name","kind":"name","label":{"en":"Your name","es":"Tu nombre"},"choices":[],"required":true}],"on_screen":{"services":true,"places":true,"team":false,"knowledge":false,"prices":true,"knowledge_ids":[]},"look":{"business_name":"Clínica Sol","logo_url":null,"accent":"#0b7476","theme":"dark","background":"soft","font":"nutalk","orb":"sphere","motion":"full","notes":"beside","captions":true,"texts":{"en":{"title":"Talk with Sofía","sub":"Book your visit in a minute.","start":"Start talking"},"es":{"title":"Habla con Sofía","sub":"Agenda tu visita en un minuto.","start":"Empezar a hablar"}}},"allowed_domains":["clinicasol.mx"],"max_call_seconds":600,"consent":{"en":"","es":""}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateLiveRoom"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The channel isn't a Live Room (`details.reason` `not_live_room`). `validation_error`: A setting the room refuses (`details.field` names it, as `look.business_name` or `questions.2`): a question without its own id or wording, a choice question without 2 to 8 choices, a website that isn't an address, a reserved or malformed link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key can't change channels (owner, admin or builder), has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The link is taken (`details.reason` `SLUG_TAKEN`, with a free one in `details.suggestion`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/ai-testers":{"get":{"operationId":"listChannelTesters","summary":"List a channel's testers","description":"Who the AI answers while the channel is in Testing (AI mode `testing`): a WhatsApp or Apple Messages channel's test numbers (`kind` `phone`), or the people who are a Messenger or Instagram channel's testers (`kind` `messenger` or `instagram`, with their name and chat), and how each was added (`added_via`: `number`, `chat` or `code`). With the AI's last reply to each and whether it remembers earlier messages.\n\nRequires the `channels:read` scope.","tags":["Channels"],"x-required-scope":"channels:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#listChannelTesters"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A WhatsApp channel's testers: its test numbers.","content":{"application/json":{"example":{"success":true,"data":[{"id":"3d5e7f9a-1b2c-4d4e-8f6a-7b8c9d0e1f2a","kind":"phone","phone_number":"+525512345678","name":null,"conversation_id":null,"added_via":"number","code_id":null,"created_at":"2026-10-20T16:00:00.000Z","last_ai_reply_at":"2026-10-26T18:20:00.000Z","last_ai_reply_status":"sent","remembers":true,"last_reset_at":null}]}}}},"400":{"description":"`validation_error`: The channel isn't a WhatsApp, Apple Messages, Messenger or Instagram channel (`details.reason` `not_testing_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"addChannelTester","summary":"Add a tester from a chat","description":"Makes the person of one of a Messenger or Instagram channel's chats a tester: while the channel is in Testing the AI answers them from their next message. At most 10 per channel. Find the chat with GET /v1/conversations?channel_id=…. Answers the channel's testers. Only add your team's own people.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#addChannelTester"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversation_id":{"description":"One of the channel's chats: its person becomes a tester.","type":"string","format":"uuid"}},"required":["conversation_id"]},"example":{"conversation_id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13"}}}},"responses":{"201":{"description":"Added.","content":{"application/json":{"example":{"success":true,"data":[{"id":"4e6f8a0b-2c3d-4e5f-9a7b-8c9d0e1f2a3b","kind":"messenger","phone_number":null,"name":"Ana López","conversation_id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","added_via":"chat","code_id":null,"created_at":"2026-10-27T15:41:05.000Z","last_ai_reply_at":"2026-10-27T15:43:20.000Z","last_ai_reply_status":"delivered","remembers":false,"last_reset_at":null}]}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"addChannelTester"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The channel isn't a WhatsApp, Apple Messages, Messenger or Instagram channel (`details.reason` `not_testing_channel`). `validation_error`: The channel is a WhatsApp or Apple Messages channel, whose testers are phone numbers: add one with POST /v1/channels/{id}/ai-test-numbers (`details.reason` `use_test_numbers`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner or admin (the role the dashboard asks for testers), has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel or conversation (a chat of another channel) with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The person is already a tester (`TESTER_EXISTS`), or the channel has 10 (`TESTERS_LIMIT`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/ai-testers/{tester_id}":{"delete":{"operationId":"removeChannelTester","summary":"Remove a tester","description":"The AI stops answering the tester (a test number or a person) while the channel is in Testing; their chat goes back to your team. A stop, so it never waits for an owner's approval.\n\nRequires the `channels:write` scope.\n\nA change runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#removeChannelTester"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"tester_id","in":"path","required":true,"description":"The tester's id, from GET /v1/channels/{id}/ai-testers (a test number's is its id in ai-test-numbers).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Removed.","content":{"application/json":{"example":{"success":true,"data":{"id":"4e6f8a0b-2c3d-4e5f-9a7b-8c9d0e1f2a3b","deleted":true}}}}},"400":{"description":"`validation_error`: The channel isn't a WhatsApp, Apple Messages, Messenger or Instagram channel (`details.reason` `not_testing_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner or admin (the role the dashboard asks for testers), has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel or tester with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/ai-testers/{tester_id}/start-over":{"post":{"operationId":"startOverChannelTester","summary":"Start the AI over with a tester","description":"The tester's next message is answered as if it were the first: the AI forgets the earlier messages (`messages`), what it remembers about the person (`memory`), or both. Nothing is deleted. A test number starts over as a person, in every chat of that phone in the workspace; a Messenger or Instagram tester in their chat on this channel.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA change runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#startOverChannelTester"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"tester_id","in":"path","required":true,"description":"The tester's id, from GET /v1/channels/{id}/ai-testers (a test number's is its id in ai-test-numbers).","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"description":"Forget the earlier messages.","type":"boolean"},"memory":{"description":"Forget what it remembers about the person.","type":"boolean"}},"required":["messages","memory"]},"example":{"messages":true,"memory":true}}}},"responses":{"200":{"description":"Started over.","content":{"application/json":{"example":{"success":true,"data":{"id":"4e6f8a0b-2c3d-4e5f-9a7b-8c9d0e1f2a3b","messages":true,"memory":true,"started_over":true}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"startOverChannelTester"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The channel isn't a WhatsApp, Apple Messages, Messenger or Instagram channel (`details.reason` `not_testing_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key can't start a tester over (owners, admins, builders and operators can), has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel or tester with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The Messenger or Instagram tester's chat is gone, so there's nothing to start over (`NO_CHAT`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/ai-testers/codes":{"post":{"operationId":"createChannelTesterCode","summary":"Make a tester code","description":"Makes a tester code for a Messenger or Instagram channel, as \"Add me with a code\" does: for 15 minutes, whoever sends it as a message to the Page or account (`link` opens that chat) becomes a tester, up to the channel's 10, and NuTalk answers them \"You're a tester\". The code is answered only here, once: NuTalk keeps its sha256, never the code. Share it with your team only. GET /v1/channels/{id}/ai-testers then lists who it added (their `code_id`).\n\nRequires the `channels:write` scope.\n\nA change runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#createChannelTesterCode"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"The code.","content":{"application/json":{"example":{"success":true,"data":{"id":"6a8c0e2f-4b5d-4f7a-8c9e-0a1b2c3d4e5f","code":"NT-48Q2","expires_at":"2026-10-27T15:56:00.000Z","link":"https://m.me/104000000000001","asset_name":"Café Aurora"}}}}},"400":{"description":"`validation_error`: The channel isn't a WhatsApp, Apple Messages, Messenger or Instagram channel (`details.reason` `not_testing_channel`). `validation_error`: The channel is a WhatsApp or Apple Messages channel, whose testers are phone numbers: add one with POST /v1/channels/{id}/ai-test-numbers (`details.reason` `use_test_numbers`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner or admin (the role the dashboard asks for testers), has left the workspace, or the key is a reseller key. `forbidden`: The key's changes wait for an owner's or admin's approval (`details.reason` `CHANGES_WAIT_FOR_APPROVAL`): a code makes whoever sends it a tester at once, so such a key adds testers from their chats instead, which waits. A control plane key makes none either (`CONTROL_PLANE_KEY`): the control plane keeps every answer, and a code is never kept.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/whatsapp-templates":{"get":{"operationId":"listWhatsAppTemplates","summary":"List a WhatsApp number's templates","description":"The templates of the number's WhatsApp account, each language on its own, from the copy NuTalk keeps (Meta's updates keep it current; `synced_at` says when NuTalk last read the whole account). `fields` lists what a sender gives when sending one, with what NuTalk fills each from (`source`, set with PATCH …/fields). Send an approved one with POST /v1/conversations/{id}/messages. Deleted templates only with `status=DELETED`.\n\nRequires the `channels:read` scope.\n\nRead as the member who created the key (any role that sees the workspace). A reseller key is refused.","tags":["Channels"],"x-required-scope":"channels:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#listWhatsAppTemplates"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Only this status; `all` is every one but deleted.","schema":{"type":"string","enum":["all","APPROVED","PENDING","REJECTED","PAUSED","DISABLED","IN_APPEAL","PENDING_DELETION","DELETED","LIMIT_EXCEEDED","ARCHIVED","UNKNOWN"],"default":"all"}},{"name":"category","in":"query","required":false,"description":"Only this category.","schema":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"]}},{"name":"search","in":"query","required":false,"description":"Words in the name or the text.","schema":{"type":"string","maxLength":100}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}}],"responses":{"200":{"description":"The templates, the ones waiting for review first.","content":{"application/json":{"example":{"success":true,"data":[{"id":"1234567890123456","name":"appointment_reminder","language":"es_MX","category":"UTILITY","status":"APPROVED","display_name":"Appointment reminder","components":[{"type":"BODY","text":"Hola {{first_name}}, te esperamos el {{date}} a las {{time}}.","example":{"body_text_named_params":[{"param_name":"first_name","example":"Ana"}]}}],"fields":[{"part":"body","key":"first_name","source_key":"first_name","button":null,"source":"contact.first_name","label":"First name","fallback":"paciente","example":"Ana"},{"part":"body","key":"date","source_key":"date","button":null,"source":"appointment.date","label":"Date","fallback":null,"example":"24 de octubre"}],"quality_score":{"score":"GREEN"},"rejected_reason":null,"rejection_info":null,"pause":null,"paused_until":null,"archived_at":null,"locked":false,"locked_note":null,"previous_category":null,"submitted_at":"2026-10-20T16:00:00.000Z","status_since":"2026-10-20T16:04:12.000Z"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false},"synced_at":"2026-10-26T17:55:00.000Z"}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: The channel isn't a WhatsApp API number (`details.reason` `not_whatsapp_api_number`): a number linked by QR has no templates or business profile of its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createWhatsAppTemplate","summary":"Create a WhatsApp template","description":"Sends a new template to Meta for review, as Manage Templates does. It belongs to the number's WhatsApp account (every channel of that account sees it) and is `PENDING` until Meta decides. Name it with lowercase letters, digits and _; give examples of its fields.\n\nRequires the `channels:write` scope.\n\nIt runs the dashboard's own rules as the member who created the key: templates and the business profile belong to the WhatsApp account, so that member must be an owner or admin. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#createWhatsAppTemplate"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Lowercase letters, digits and _, starting with a letter.","type":"string","maxLength":512},"language":{"description":"Meta's language code (`es_MX`).","type":"string"},"category":{"description":"Meta's category.","type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"]},"components":{"description":"The message's parts: a BODY, and optionally a HEADER, a FOOTER and BUTTONS.","type":"array","items":{"description":"A part.","type":"object","properties":{"type":{"description":"The part of the message.","type":"string","enum":["HEADER","BODY","FOOTER","BUTTONS"]},"format":{"description":"A header's kind.","type":"string","enum":["TEXT","IMAGE","VIDEO","DOCUMENT"]},"text":{"description":"The text, with numbered fields ({{1}}); the body takes up to 1,024 characters, the footer 60.","type":"string"},"example":{"description":"Examples of the fields for Meta's review: `header_text` (a list), `body_text` (a list of lists).","type":"object","additionalProperties":true},"buttons":{"description":"For BUTTONS: the buttons.","type":"array","items":{"description":"A button.","type":"object","properties":{"type":{"description":"What it does.","type":"string","enum":["PHONE_NUMBER","URL","QUICK_REPLY","COPY_CODE"]},"text":{"description":"Its label.","type":"string","maxLength":25},"phone_number":{"description":"For PHONE_NUMBER.","type":"string"},"url":{"description":"For URL.","type":"string","format":"uri"},"example":{"description":"Examples of a link's variable end.","type":"array","items":{"description":"An example.","type":"string"}}},"required":["type","text"]}}},"required":["type"]}}},"required":["name","language","category","components"]},"example":{"name":"appointment_reminder","language":"es_MX","category":"UTILITY","components":[{"type":"BODY","text":"Hola {{1}}, te esperamos el {{2}}.","example":{"body_text":[["Ana","24 de octubre"]]}}]}}}},"responses":{"201":{"description":"Sent for review.","content":{"application/json":{"example":{"success":true,"data":{"id":"1234567890123456","name":"appointment_reminder","language":"es_MX","category":"UTILITY","status":"PENDING"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The channel isn't a WhatsApp API number (`details.reason` `not_whatsapp_api_number`): a number linked by QR has no templates or business profile of its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key wasn't created by an owner or admin, its creator has left the workspace, or it is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The account already has a template with this name. `conflict`: The number's connection to Meta expired (`details.reason` `connection_expired`): reconnect it in NuTalk.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: The account reached Meta's limit on new templates (100 an hour).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: Meta didn't answer: try again later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/{id}/whatsapp-templates/{template_name}":{"get":{"operationId":"getWhatsAppTemplate","summary":"Get a WhatsApp template","description":"One template of the number's WhatsApp account, every language of it, from NuTalk's copy.\n\nRequires the `channels:read` scope.\n\nRead as the member who created the key (any role that sees the workspace). A reseller key is refused.","tags":["Channels"],"x-required-scope":"channels:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#getWhatsAppTemplate"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"template_name","in":"path","required":true,"description":"The template's name: lowercase letters, digits and _, as WhatsApp holds it.","schema":{"type":"string","maxLength":512}}],"responses":{"200":{"description":"The template's languages.","content":{"application/json":{"example":{"success":true,"data":{"name":"appointment_reminder","languages":[{"id":"1234567890123456","name":"appointment_reminder","language":"es_MX","category":"UTILITY","status":"APPROVED","display_name":"Appointment reminder","components":[{"type":"BODY","text":"Hola {{first_name}}, te esperamos el {{date}} a las {{time}}.","example":{"body_text_named_params":[{"param_name":"first_name","example":"Ana"}]}}],"fields":[{"part":"body","key":"first_name","source_key":"first_name","button":null,"source":"contact.first_name","label":"First name","fallback":"paciente","example":"Ana"},{"part":"body","key":"date","source_key":"date","button":null,"source":"appointment.date","label":"Date","fallback":null,"example":"24 de octubre"}],"quality_score":{"score":"GREEN"},"rejected_reason":null,"rejection_info":null,"pause":null,"paused_until":null,"archived_at":null,"locked":false,"locked_note":null,"previous_category":null,"submitted_at":"2026-10-20T16:00:00.000Z","status_since":"2026-10-20T16:04:12.000Z"}]}}}}},"400":{"description":"`validation_error`: The channel isn't a WhatsApp API number (`details.reason` `not_whatsapp_api_number`): a number linked by QR has no templates or business profile of its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel or template with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteWhatsAppTemplate","summary":"Delete a WhatsApp template","description":"Deletes the template from the number's WhatsApp account at Meta: every language of it, or only the one `language` names. It is gone for every channel and campaign of that account, and a campaign step that sends it stops working.\n\nRequires the `channels:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nIt runs the dashboard's own rules as the member who created the key: templates and the business profile belong to the WhatsApp account, so that member must be an owner or admin. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#deleteWhatsAppTemplate"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"template_name","in":"path","required":true,"description":"The template's name: lowercase letters, digits and _, as WhatsApp holds it.","schema":{"type":"string","maxLength":512}},{"name":"language","in":"query","required":false,"description":"Only this language, as Meta codes it (`es_MX`); without it, every language of the template.","schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"example":{"success":true,"data":{"name":"appointment_reminder","language":"es_MX","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteWhatsAppTemplate"}}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: The channel isn't a WhatsApp API number (`details.reason` `not_whatsapp_api_number`): a number linked by QR has no templates or business profile of its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key wasn't created by an owner or admin, its creator has left the workspace, or it is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No such channel, or WhatsApp holds no such template (`details.reason` `TEMPLATE_NOT_FOUND`: it is already gone).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The number's connection to Meta expired (`details.reason` `connection_expired`): reconnect it in NuTalk.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: Meta didn't answer: try again later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/{id}/whatsapp-templates/{template_name}/unarchive":{"post":{"operationId":"restoreWhatsAppTemplate","summary":"Restore an archived WhatsApp template","description":"Restores one archived language of a template: WhatsApp archives a template unused for 12 months, and restoring it brings back the status it had. Its status reads `UNKNOWN` until NuTalk's next read of the account says which.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nIt runs the dashboard's own rules as the member who created the key: templates and the business profile belong to the WhatsApp account, so that member must be an owner or admin. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#restoreWhatsAppTemplate"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"template_name","in":"path","required":true,"description":"The template's name: lowercase letters, digits and _, as WhatsApp holds it.","schema":{"type":"string","maxLength":512}},{"name":"language","in":"query","required":true,"description":"The language, as Meta codes it (`es_MX`, `en_US`, `es`): each language of a template is its own.","schema":{"type":"string"}}],"responses":{"200":{"description":"Restored.","content":{"application/json":{"example":{"success":true,"data":{"name":"appointment_reminder","language":"es_MX","restored":true}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"restoreWhatsAppTemplate"}}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: The channel isn't a WhatsApp API number (`details.reason` `not_whatsapp_api_number`): a number linked by QR has no templates or business profile of its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key wasn't created by an owner or admin, its creator has left the workspace, or it is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel or template with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: WhatsApp didn't restore it (its own reason in the message). `conflict`: The number's connection to Meta expired (`details.reason` `connection_expired`): reconnect it in NuTalk.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: Meta didn't answer: try again later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/{id}/whatsapp-templates/{template_name}/fields":{"patch":{"operationId":"setWhatsAppTemplateFieldSources","summary":"Set what fills a WhatsApp template's fields","description":"Saves what fills each field of one language of a template when someone sends it, as the Templates tab does: the contact's data, one of the workspace's contact fields, the contact's next appointment, the business's name, or typed by the sender. `field_sources` is keyed by the field's `source_key` (GET lists it: `first_name`, `1`, `header.1`) and replaces the language's sources. It stays in NuTalk: WhatsApp never sees it, so it needs no review.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nIt runs the dashboard's own rules as the member who created the key: templates and the business profile belong to the WhatsApp account, so that member must be an owner or admin. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#setWhatsAppTemplateFieldSources"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}},{"name":"template_name","in":"path","required":true,"description":"The template's name: lowercase letters, digits and _, as WhatsApp holds it.","schema":{"type":"string","maxLength":512}},{"name":"language","in":"query","required":true,"description":"The language, as Meta codes it (`es_MX`, `en_US`, `es`): each language of a template is its own.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"field_sources":{"description":"By the field's `source_key`, up to 50 fields, each `{label, source, fallback}`: `label` (up to 40 characters) is how the field is called; `source` is what fills it: `contact.first_name`, `contact.last_name`, `contact.full_name`, `contact.phone`, `contact.email`, `contact.custom.<field key>`, `appointment.date`, `appointment.time`, `appointment.service`, `appointment.with`, `appointment.place`, `business.name`, or `ask` (the sender types it); `fallback` (up to 200, optional) is what goes for a contact source when the contact has no value.","type":"object","additionalProperties":true}},"required":["field_sources"]},"example":{"field_sources":{"first_name":{"label":"First name","source":"contact.first_name","fallback":"paciente"},"date":{"label":"Date","source":"appointment.date"}}}}}},"responses":{"200":{"description":"Saved.","content":{"application/json":{"example":{"success":true,"data":{"name":"appointment_reminder","language":"es_MX","field_sources":{"first_name":{"label":"First name","source":"contact.first_name","fallback":"paciente"},"date":{"label":"Date","source":"appointment.date","fallback":null}}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setWhatsAppTemplateFieldSources"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A name isn't a field of this template, or a contact field isn't the workspace's (`details.fields` lists them). `validation_error`: The channel isn't a WhatsApp API number (`details.reason` `not_whatsapp_api_number`): a number linked by QR has no templates or business profile of its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key wasn't created by an owner or admin, its creator has left the workspace, or it is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel or template with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/channels/{id}/whatsapp-profile":{"get":{"operationId":"getWhatsAppProfile","summary":"Get a WhatsApp number's business profile","description":"The business profile customers see on the number (about, description, category, address, email, websites, photo) as WhatsApp last answered it, asked again when older than five minutes, with its name and blue check. `two_step_pin` says whether NuTalk keeps the number's two-step PIN, never the PIN; `refusal` says why WhatsApp's last ask got no answer.\n\nRequires the `channels:read` scope.\n\nRead as the member who created the key (any role that sees the workspace). A reseller key is refused.","tags":["Channels"],"x-required-scope":"channels:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#getWhatsAppProfile"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The profile.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","profile":{"about":"Citas de lunes a sábado.","description":"Clínica dental en Monterrey.","category":"HEALTH","address":"Av. Constitución 100, Monterrey","email":"citas@clinic.example.com","websites":["https://clinic.example.com"],"photo_url":null},"number":{"display_phone_number":"+52 81 5555 0123","verified_name":"Clinica Dental","name_status":"APPROVED","is_official":false,"blue_check_status":null,"business_verification":"verified"},"answered_at":"2026-10-26T18:00:00.000Z","refusal":null,"two_step_pin":true,"can_edit":true}}}}},"400":{"description":"`validation_error`: The channel isn't a WhatsApp API number (`details.reason` `not_whatsapp_api_number`): a number linked by QR has no templates or business profile of its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: Meta didn't answer: try again later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateWhatsAppProfile","summary":"Change a WhatsApp number's business profile","description":"Changes the fields sent on WhatsApp, as the Profile tab does: only those go to WhatsApp, the others stay as they are there. Customers see the change at once. NuTalk checks WhatsApp's limits first; when WhatsApp still refuses, nothing changes and its own words come back with the field they name. The name, the photo and the blue check stay in the dashboard (the channel's Profile tab).\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nIt runs the dashboard's own rules as the member who created the key: templates and the business profile belong to the WhatsApp account, so that member must be an owner or admin. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#updateWhatsAppProfile"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"about":{"description":"The short line under the name.","type":"string","minLength":1,"maxLength":139},"description":{"description":"What the business does.","type":"string","maxLength":512},"category":{"description":"Meta's category; empty for none.","type":"string","enum":["ALCOHOL","APPAREL","AUTO","BEAUTY","EDU","ENTERTAIN","EVENT_PLAN","FINANCE","GOVT","GROCERY","HEALTH","HOTEL","NONPROFIT","ONLINE_GAMBLING","OTC_DRUGS","OTHER","PHYSICAL_GAMBLING","PROF_SERVICES","RESTAURANT","RETAIL","TRAVEL",""]},"address":{"description":"The address.","type":"string","maxLength":256},"email":{"description":"A contact email.","type":"string","maxLength":128},"websites":{"description":"Up to two websites; the list replaces the ones WhatsApp shows.","type":"array","maxItems":2,"items":{"description":"An http(s) link.","type":"string","maxLength":256}}}},"example":{"about":"Citas de lunes a sábado, 9 a 19 h.","websites":["https://clinic.example.com"]}}}},"responses":{"200":{"description":"The profile as WhatsApp now shows it.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","profile":{"about":"Citas de lunes a sábado.","description":"Clínica dental en Monterrey.","category":"HEALTH","address":"Av. Constitución 100, Monterrey","email":"citas@clinic.example.com","websites":["https://clinic.example.com"],"photo_url":null},"number":{"display_phone_number":"+52 81 5555 0123","verified_name":"Clinica Dental","name_status":"APPROVED","is_official":false,"blue_check_status":null,"business_verification":"verified"},"answered_at":"2026-10-26T18:00:00.000Z","refusal":null,"two_step_pin":true,"can_edit":true}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateWhatsAppProfile"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A field breaks WhatsApp's limits (`details.issues`), or WhatsApp refused it (`details.reason` `refused`, `details.field`). `validation_error`: The channel isn't a WhatsApp API number (`details.reason` `not_whatsapp_api_number`): a number linked by QR has no templates or business profile of its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key wasn't created by an owner or admin, its creator has left the workspace, or it is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: WhatsApp hasn't answered with the profile yet (`profile_not_read`), or the number's connection can't change it (`no_access`, `expired`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: Meta didn't answer: try again later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/{id}/whatsapp-template-hold":{"post":{"operationId":"retryWhatsAppTemplateHold","summary":"Try a held WhatsApp account's templates again","description":"\"I added a card\": when Meta refused a template for payment, NuTalk holds the account's templates (a send answers `whatsapp_payment_hold`) and pauses the campaigns that send them. After you add a payment method in Meta, this sends the account's next template to Meta as a try and starts the paused campaigns again so one of theirs can be it. Meta's answer to the try ends the hold, or keeps it. The campaigns it starts again send to customers, so it needs `campaigns:write` as well.\n\nRequires the `channels:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nIt runs the dashboard's own rules as the member who created the key: templates and the business profile belong to the WhatsApp account, so that member must be an owner or admin. A key whose creator has left the workspace, and a reseller key, are refused.\n\nMeta charges the try, and the campaigns that start again send to customers: confirm with the user first.","tags":["Channels"],"x-required-scope":"channels:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels#retryWhatsAppTemplateHold"},"parameters":[{"name":"id","in":"path","required":true,"description":"The channel id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The try is asked for.","content":{"application/json":{"example":{"success":true,"data":{"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","held_at":"2026-10-25T15:00:00.000Z","refused_at":"2026-10-26T09:00:00.000Z","try_requested_at":"2026-10-26T18:10:00.000Z","campaigns_resumed":2}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"retryWhatsAppTemplateHold"}}}}}},"400":{"description":"`validation_error`: The channel isn't a WhatsApp API number (`details.reason` `not_whatsapp_api_number`): a number linked by QR has no templates or business profile of its own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key lacks `campaigns:write` (the campaigns the try starts again send to customers), or it wasn't created by an owner or admin, its creator has left the workspace, or it is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No channel with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The account's templates aren't on hold.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/phone-numbers":{"get":{"operationId":"listPhoneNumbers","summary":"List phone numbers","description":"The workspace's phone numbers, newest first: the ones bought through NuTalk and its own numbers verified as caller ID, with their status, the channel their calls ring, and what the workspace pays for them.\n\nRequires the `phone_numbers:read` scope.\n\nFields: `id`, `phone_number`, `country`, `type`, `status`, `capabilities`, `receives_calls_on`, `billing`, `created_at`.","tags":["Phone numbers"],"x-required-scope":"phone_numbers:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/phone-numbers#listPhoneNumbers"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of numbers. Each has the fields below.","content":{"application/json":{"example":{"success":true,"data":[{"id":"2f6d8b0a-4c1e-4f3a-9b5d-7e9f1a3c5b72","phone_number":"+528155550123","country":"MX","type":"purchased","status":"active","capabilities":{"voice":true,"sms":false},"receives_calls_on":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support line"},"billing":{"included":false,"monthly_price":{"amount_cents":30000,"currency":"mxn"},"next_billing_at":"2026-11-23T18:20:05.000Z"},"created_at":"2026-10-23T18:20:05.117+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"buyPhoneNumber","summary":"Buy a phone number","description":"Asks to buy a number found with `GET /v1/phone-numbers/available`. **It always waits for an owner's or admin's approval**, whatever the key's approval settings: the call answers `202` with `data.change_request` (status `pending`) and `data.purchase` (the number, the currency and the price), and nothing is charged until an owner or admin approves it in NuTalk (Settings → API keys → Approvals), where they see the number, the price, the monthly cost and the card. Follow it with `GET /v1/change-requests/:id`: once approved, the workspace's card is charged for the first month and the number is ordered from the carrier; the request's `result` is the number, as `GET /v1/phone-numbers/:id` answers it.\n\nRequires the `phone_numbers:write` scope.\n\nWhatever the key asks, this call waits for an owner's or admin's approval: it answers 202 with a change request, and runs once an owner or admin approves it.\n\nEvery check is made when you ask, so a purchase the workspace can't make is refused at once instead of waiting; they are all made again when it is approved.\n\nOnce approved, a card that is declined fails the request (its `result` is a `402` `payment_failed`, with `details.reason` `CARD_DECLINED`), and a number the carrier can no longer sell (it was taken while the purchase waited) is refunded at once (`502`, `details.refunded`).\n\nCards are added in NuTalk (Settings → Billing), never through the API, and the charge goes to the workspace's default card. A card that needs 3-D Secure is declined here (`402`): pay with another card, or buy the number in the dashboard.\n\nOnly a key created by an owner or admin buys, and only owners and admins can give a key `phone_numbers:write`. A reseller key never buys.","tags":["Phone numbers"],"x-required-scope":"phone_numbers:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/phone-numbers#buyPhoneNumber"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_number":{"description":"The number to buy, in E.164 (+528155550123).","type":"string"},"country":{"description":"For a +1 number only: `CA` when it's Canadian (default `US`).","type":"string","enum":["US","CA","MX"]},"currency":{"description":"Required when the workspace doesn't pay in a currency yet (this purchase sets it for good); otherwise its currency, and another one is refused.","type":"string","enum":["usd","mxn"]},"monthly_price_cents":{"description":"The monthly price you were quoted (`price.monthly_price_cents`): a purchase at any other price is refused. Without it, the price when you ask is the one approved.","type":"integer","minimum":1},"compliance_profile_id":{"description":"Mexico: the company profile to order the number under (default: the workspace's best one). Profiles are completed once in NuTalk (Phone numbers → Compliance).","type":["string","null"],"format":"uuid"}},"required":["phone_number"]},"example":{"phone_number":"+528155550123","currency":"mxn","monthly_price_cents":30000}}}},"responses":{"201":{"description":"The approved purchase, as the change request's `result` holds it: the new number. It takes calls once it is on a voice channel; a Mexican number is first reviewed by the carrier (`carrier_review`).","content":{"application/json":{"example":{"success":true,"data":{"id":"2f6d8b0a-4c1e-4f3a-9b5d-7e9f1a3c5b72","phone_number":"+528155550123","country":"MX","type":"purchased","status":"active","capabilities":{"voice":true,"sms":false},"receives_calls_on":null,"billing":{"included":false,"monthly_price":{"amount_cents":30000,"currency":"mxn"},"next_billing_at":"2026-11-23T18:20:05.000Z"},"created_at":"2026-10-23T18:20:05.117+00:00"}}}}},"202":{"description":"The purchase waits for approval. Nothing was charged; the price and the compliance profile are fixed in the request.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"3f2b1c9e-6d4a-4e8b-9c1f-2a5d7e9b0c34","status":"pending","approval":"change","operation":"buyPhoneNumber","method":"POST","path":"/v1/phone-numbers","summary":"Buy a phone number (POST /v1/phone-numbers)","created_at":"2026-10-23T18:20:05.117+00:00","expires_at":"2026-10-24T18:20:05.117+00:00","decided_at":null,"finished_at":null,"result":null},"purchase":{"phone_number":"+528155550123","country":"MX","currency":"mxn","charged_today_cents":30000,"monthly_price_cents":30000,"compliance_profile_id":"8f9e6d2c-3b4a-4c5d-8e7f-0a1b2c3d4e5f"}},"message":"Buying a number waits for an owner's or admin's approval in NuTalk: nothing is charged until it is approved. Check it with GET /v1/change-requests/{id}."}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The number is not in the US, Canada or Mexico, or `currency` is missing for a workspace that doesn't pay in one yet (`CURRENCY_REQUIRED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key's creator is not an owner or admin, has left the workspace, or number purchases are not enabled for the workspace (`details.reason` `PURCHASES_NOT_ENABLED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The workspace already has the number (`ALREADY_OWNED`), it isn't available (`NUMBER_NOT_AVAILABLE`), an earlier purchase of it is being refunded or reviewed (`REFUND_IN_PROGRESS`, `PURCHASE_IN_REVIEW`), the workspace has no billing account or card on file (`NO_BILLING_ACCOUNT`, `NO_PAYMENT_METHOD`), pays in another currency (`BILLING_CURRENCY_MISMATCH`), the price changed (`PRICE_CHANGED`, with the new price), or a Mexican number has no usable compliance profile (`NO_REGULATORY_BUNDLE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: NuTalk doesn't sell numbers in the country right now (`PURCHASES_UNAVAILABLE`, `PRICE_UNAVAILABLE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/phone-numbers/{id}":{"get":{"operationId":"getPhoneNumber","summary":"Get a phone number","description":"One of the workspace's phone numbers.\n\nRequires the `phone_numbers:read` scope.","tags":["Phone numbers"],"x-required-scope":"phone_numbers:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/phone-numbers#getPhoneNumber"},"parameters":[{"name":"id","in":"path","required":true,"description":"The phone number id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The number.","content":{"application/json":{"example":{"success":true,"data":{"id":"2f6d8b0a-4c1e-4f3a-9b5d-7e9f1a3c5b72","phone_number":"+528155550123","country":"MX","type":"purchased","status":"active","capabilities":{"voice":true,"sms":false},"receives_calls_on":{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support line"},"billing":{"included":false,"monthly_price":{"amount_cents":30000,"currency":"mxn"},"next_billing_at":"2026-11-23T18:20:05.000Z"},"created_at":"2026-10-23T18:20:05.117+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No phone number with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"removeVerifiedCallerId","summary":"Remove a verified caller ID","description":"Removes one of the workspace's own numbers verified as caller ID (`type` `verified`), as the Phone numbers page does: from the carrier, then from the workspace, so it can be verified again in another workspace. A number bought through NuTalk is never removed here. A caller ID that a phone channel still calls from is refused (`409`, `IN_USE`) with those channels and the campaigns calling through them, unless you confirm them: `detach_channels: true` with `channel_ids` naming every one of them. They then stop calling from it; a channel you didn't name is never cleared.\n\nRequires the `phone_numbers:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nNothing brings the caller ID back: it must be verified again, in the dashboard. Confirm with the user first.\n\nWhen the key asks for approval on deletes, the removal waits for an owner's or admin's approval.","tags":["Phone numbers"],"x-required-scope":"phone_numbers:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/phone-numbers#removeVerifiedCallerId"},"parameters":[{"name":"id","in":"path","required":true,"description":"The phone number id.","schema":{"type":"string","format":"uuid"}},{"name":"detach_channels","in":"query","required":false,"description":"`true`: remove it from the channels in `channel_ids` too (or send it in the body).","schema":{"type":"string","enum":["true","false"]}},{"name":"channel_ids","in":"query","required":false,"description":"The channels confirmed, comma-separated (or send them in the body).","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"detach_channels":{"description":"`true`: the channels in `channel_ids`, which call from the number, stop calling from it, and it is removed.","type":"boolean"},"channel_ids":{"description":"Required with `detach_channels`: every channel that calls from the number, as the `409` named them. A channel calling from it that isn't here refuses the removal again.","type":"array","maxItems":100,"items":{"description":"A channel id.","type":"string","format":"uuid"}}}},"example":{"detach_channels":true,"channel_ids":["9f19ef32-eaef-452c-bebc-14d9759cdb64"]}}}},"responses":{"200":{"description":"The caller ID is removed.","content":{"application/json":{"example":{"success":true,"data":{"phone_number_id":"2f6d8b0a-4c1e-4f3a-9b5d-7e9f1a3c5b72","phone_number":"+528155550123","removed":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"removeVerifiedCallerId"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: The number is not a verified caller ID (`details.reason` `not_verified_caller_id`), or `detach_channels` came without `channel_ids` (`channel_ids_required`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key's creator has a role that can't remove numbers (owners, admins and builders can) or has left the workspace, the key is a reseller key, or a channel to clear is the Zoom channel and the creator isn't an owner or admin (`ZOOM_MANAGERS_ONLY`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No phone number with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: Phone channels call from it (`details.reason` `IN_USE`, with `details.channels` (`id`, `name`) and `details.campaigns` (`id`, `name`, `channel_id`)), or the carrier can't remove this caller ID here (`CARRIER_UNSUPPORTED`: contact support).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: The carrier didn't answer (`CARRIER_UNAVAILABLE`) or isn't set up (`CARRIER_NOT_CONFIGURED`): nothing was removed; try again later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/phone-numbers/available":{"get":{"operationId":"searchAvailablePhoneNumbers","summary":"Search numbers to buy","description":"Numbers the carrier has for sale in the United States, Canada or Mexico (local numbers that take calls), by area code or, in Mexico, by city name. Easy-to-remember numbers come first (`memorable`). `price` is what this workspace pays for any of them: the first month when the purchase is approved, then the same every month, in the workspace's billing currency.\n\nRequires the `phone_numbers:read` scope.\n\nAn empty `data` means the carrier has no number for sale there now; try another area code or city.","tags":["Phone numbers"],"x-required-scope":"phone_numbers:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/phone-numbers#searchAvailablePhoneNumbers"},"parameters":[{"name":"country","in":"query","required":true,"description":"The country.","schema":{"type":"string","enum":["US","CA","MX"]}},{"name":"area_code","in":"query","required":false,"description":"Only numbers with this area code (2 or 3 digits: 81 Monterrey, 212 New York).","schema":{"type":"string"}},{"name":"city","in":"query","required":false,"description":"Mexico only: the city's name instead of its area code (`Monterrey`, `CDMX`, `Querétaro`). A city with two area codes (Mexico City: 55 and 56) is searched in both.","schema":{"type":"string","maxLength":80}},{"name":"currency","in":"query","required":false,"description":"The currency to price in when the workspace doesn't pay in one yet (`price.currency_locked` false). Otherwise the workspace's currency is used.","schema":{"type":"string","enum":["usd","mxn"]}},{"name":"limit","in":"query","required":false,"description":"Numbers to return.","schema":{"type":"integer","default":20,"minimum":1,"maximum":50}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The numbers for sale and their price.","content":{"application/json":{"example":{"success":true,"data":[{"phone_number":"+528155550123","country":"MX","locality":"Monterrey","region":"NL","capabilities":{"voice":true,"sms":false},"memorable":true}],"price":{"currency":"mxn","charged_today_cents":30000,"monthly_price_cents":30000,"currency_locked":true}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: `city` names no Mexican city NuTalk sells numbers in (`details.reason` `UNKNOWN_CITY`), or is used outside Mexico.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key's creator is not an owner or admin, has left the workspace, or number purchases are not enabled for the workspace (`details.reason` `PURCHASES_NOT_ENABLED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: NuTalk doesn't sell numbers in the country right now (`PURCHASES_UNAVAILABLE`, `PRICE_UNAVAILABLE`), or the carrier is busy or didn't answer (`CARRIER_BUSY`, `CARRIER_SEARCH_FAILED`): try again in a few seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/phone-numbers/purchase-readiness":{"get":{"operationId":"getPhoneNumberPurchaseReadiness","summary":"What a purchase needs","description":"What `POST /v1/phone-numbers` needs before you ask for it, by the rules the purchase checks: whether the key's creator may buy (`can_buy`: owners and admins), the currency the workspace pays in (`currency`; null with `currency_locked` false while it pays in none, and then the purchase must send `currency`, which sets it for good), whether the workspace has a billing account and a card on file (never which card), and, per country whose numbers need a compliance profile (`MX`), whether that profile is complete and the company profiles a purchase can be ordered under (`compliance_profile_id`).\n\nRequires the `phone_numbers:read` scope.\n\nCards are added in NuTalk (Settings → Billing), never through the API, and the charge goes to the workspace's default card. A card that needs 3-D Secure is declined here (`402`): pay with another card, or buy the number in the dashboard.","tags":["Phone numbers"],"x-required-scope":"phone_numbers:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/phone-numbers#getPhoneNumberPurchaseReadiness"},"responses":{"200":{"description":"`billing_account` and `card_on_file` are null when the key's creator can't buy: they are billing data. `regulatory[country].reason` says what is missing (`business_info_missing`, `profile_missing`, `document_missing`, `rejected`); profiles are completed in NuTalk (Phone numbers → Compliance).","content":{"application/json":{"example":{"success":true,"data":{"can_buy":true,"currency":"mxn","currency_locked":true,"billing_account":true,"card_on_file":true,"regulatory":{"MX":{"ready":true,"reason":null,"compliance_profile_id":"8f9e6d2c-3b4a-4c5d-8e7f-0a1b2c3d4e5f"}},"profiles":{"MX":[{"id":"8f9e6d2c-3b4a-4c5d-8e7f-0a1b2c3d4e5f","status":"approved","submitted":true,"label":null,"legal_name":"Clínica Dental Sonrisa, S.A. de C.V.","tax_id":"CDS010101AB1"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key's creator has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/billing":{"get":{"operationId":"getBilling","summary":"Get the plan and credits","description":"The workspace's plan, the currency it pays in (`currency` null while it pays in none yet), the current credit period, and its credits: the balance, what the period used, the plan's monthly allowance, what was bought and used of it, and whether the workspace is suspended. Never a card or a payment method.\n\nRequires the `billing_usage:read` scope.\n\nReads run as the member who created the key: they must read Billing in NuTalk now (an owner or admin). A workspace whose billing its reseller manages, a key whose creator has left or was demoted, and a reseller key are refused.","tags":["Billing"],"x-required-scope":"billing_usage:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/billing#getBilling"},"responses":{"200":{"description":"The plan and credits.","content":{"application/json":{"example":{"success":true,"data":{"plan":{"code":"growth","name":"Growth","kind":"subscription","monthly_credits":2000},"currency":"mxn","currency_locked":true,"period":{"start":"2026-10-12T00:00:00+00:00","end":"2026-11-12T00:00:00+00:00"},"credits":{"balance":1240.5,"used_this_period":759.5,"monthly_allowance":2000,"plan_credits_used":759.5,"bought":0,"bought_used":0,"overuse":0},"suspended":false,"suspension_reason":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key's creator isn't an owner or admin now, has left the workspace, or the workspace's billing is its reseller's (`details.reason` `BILLING_MANAGED_BY_RESELLER`), or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/billing/usage":{"get":{"operationId":"getBillingUsage","summary":"Get the usage report","description":"The credits used each day of a period (up to 90 days), by agent, channel and campaign, with their names, in the workspace's time zone: the report the dashboard's Usage sheet shows.\n\nRequires the `billing_usage:read` scope.\n\nReads run as the member who created the key: they must read Billing in NuTalk now (an owner or admin). A workspace whose billing its reseller manages, a key whose creator has left or was demoted, and a reseller key are refused.","tags":["Billing"],"x-required-scope":"billing_usage:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/billing#getBillingUsage"},"parameters":[{"name":"period","in":"query","required":false,"description":"The period; `custom` takes `from` and `to`.","schema":{"type":"string","enum":["last30","thisMonth","lastMonth","last3Months","custom"],"default":"last30"}},{"name":"from","in":"query","required":false,"description":"With `custom`: the first day (YYYY-MM-DD).","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"With `custom`: the last day (YYYY-MM-DD, today at the latest).","schema":{"type":"string"}}],"responses":{"200":{"description":"Each line is one day's credits of one kind (`type`, `bucket`, `kind`) for an agent, a channel and a campaign (null when it has none), with its `units` (minutes, messages…).","content":{"application/json":{"example":{"success":true,"data":{"period":"thisMonth","from":"2026-10-01","to":"2026-10-27","today":"2026-10-27","days":["2026-10-26","2026-10-27"],"lines":[{"day":"2026-10-27","type":"usage","bucket":"calls","kind":"voice_minute","agent_id":"550e8400-e29b-41d4-a716-446655440000","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","campaign_id":null,"credits":42,"units":21}],"agents":[{"id":"550e8400-e29b-41d4-a716-446655440000","name":"Sofía"}],"channels":[{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"Support line","type":"voice"}],"campaigns":[],"events":[]}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: A custom period longer than 90 days or ending after today (`details.reason` `USAGE_PERIOD_INVALID`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key's creator isn't an owner or admin now, has left the workspace, or the workspace's billing is its reseller's (`details.reason` `BILLING_MANAGED_BY_RESELLER`), or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/billing/credit-transactions":{"get":{"operationId":"listCreditTransactions","summary":"List the credit history","description":"The workspace's credit history, newest first: each grant, purchase, use and refund (`type`, a signed `amount`), the balance after it, and for a use the call, contact, test run or campaign it was for and its billed minutes. Never its payment's Stripe ids.\n\nRequires the `billing_usage:read` scope.\n\nReads run as the member who created the key: they must read Billing in NuTalk now (an owner or admin). A workspace whose billing its reseller manages, a key whose creator has left or was demoted, and a reseller key are refused.","tags":["Billing"],"x-required-scope":"billing_usage:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/billing#listCreditTransactions"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"since","in":"query","required":false,"description":"Only lines from this moment on.","schema":{"type":"string","format":"date-time"}},{"name":"until","in":"query","required":false,"description":"Only lines before this moment.","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"A page of the history.","content":{"application/json":{"example":{"success":true,"data":[{"id":"9e1d2c3b-4a5f-4e6d-8c7b-6a5f4e3d2c1b","type":"usage","amount":-2,"balance_after":1240.5,"reference_type":"call","reference_id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","description":"Voice call","call_id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","test_run_id":null,"campaign_id":null,"billed_minutes":1,"created_at":"2026-10-27T18:20:05.117+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key's creator isn't an owner or admin now, has left the workspace, or the workspace's billing is its reseller's (`details.reason` `BILLING_MANAGED_BY_RESELLER`), or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/billing/alerts":{"get":{"operationId":"getBillingAlerts","summary":"Get the billing alerts","description":"The billing alerts: low credits (and below how many), the monthly usage limit (its credits, the share at which it warns, and what it does when reached), and the other addresses the alerts are emailed to (`pending_emails` haven't confirmed yet). They are changed in NuTalk (Settings → Billing).\n\nRequires the `billing_usage:read` scope.\n\nReads run as the member who created the key: they must read Billing in NuTalk now (an owner or admin). A workspace whose billing its reseller manages, a key whose creator has left or was demoted, and a reseller key are refused.","tags":["Billing"],"x-required-scope":"billing_usage:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/billing#getBillingAlerts"},"responses":{"200":{"description":"The alerts.","content":{"application/json":{"example":{"success":true,"data":{"low_credits":{"enabled":true,"threshold":200,"default":true},"usage_limit":{"enabled":false,"credits":null,"alert_percent":80,"action":"notify"},"extra_emails":["finance@example.com"],"pending_emails":[]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key's creator isn't an owner or admin now, has left the workspace, or the workspace's billing is its reseller's (`details.reason` `BILLING_MANAGED_BY_RESELLER`), or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/webhooks":{"get":{"operationId":"listWebhooks","summary":"List webhook endpoints","description":"Lists the workspace's webhook endpoints with their events and last delivery result (every attempt is in the endpoint's delivery log). Signing secrets are never returned after creation.\n\nRequires the `webhooks:read` scope.\n\nPaging is lenient, never refused: a `limit` of 0 or not a number means 20, above 100 means 100, and a negative `offset` means 0.","tags":["Webhooks"],"x-required-scope":"webhooks:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/webhooks#listWebhooks"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of endpoints.","content":{"application/json":{"example":{"success":true,"data":[{"id":"ff19434c-ce6c-4dfd-a07d-7b367ad4396e","url":"https://hooks.example.com/nutalk","description":"Production CRM sync","events":["call.completed","call.failed"],"campaign_ids":null,"is_active":true,"last_delivery_at":null,"last_delivery_status":null,"created_at":"2026-10-02T03:44:44.387419+00:00","updated_at":"2026-10-02T03:44:44.387419+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createWebhook","summary":"Register a webhook endpoint","description":"Registers an https endpoint for events. The response carries the signing secret (`whsec_…`) once: store it to verify the `X-NuTalk-Signature` header. A workspace can have up to 20 endpoints.\n\nRequires the `webhooks:write` scope.\n\n`events` defaults to `[\"*\"]` (every call and campaign event) when omitted, which takes `calls:read`.","tags":["Webhooks"],"x-required-scope":"webhooks:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/webhooks#createWebhook"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"description":"Public URL starting with `https://` (no localhost or private addresses).","type":"string","format":"uri","maxLength":2000},"events":{"description":"Events to receive. `*` is every call and campaign event; call events (and `*`) carry each call's numbers and outcome, so they take a key that also has `calls:read`. Conversation events (`message.received`, `conversation.status_changed`, `conversation.assigned`) carry customer messages, so they must be named, with a key that has `conversations:read`. Conversion events (`conversion.recorded`, `conversion.retracted`) must be named too, with a key that has `conversions:read`, while Conversions is on for the workspace.","type":"array","minItems":1,"maxItems":16,"items":{"description":"An event name.","type":"string","enum":["*","call.started","call.completed","call.failed","campaign.enrollment.completed","message.received","conversation.status_changed","conversation.assigned","conversion.recorded","conversion.retracted"]}},"description":{"description":"What the endpoint is for.","type":"string","maxLength":500},"campaign_ids":{"description":"Only these campaigns' call and campaign events (call.started, call.completed, call.failed, campaign.enrollment.completed): a call outside every campaign, or another campaign's, isn't sent. Conversation and conversion events aren't filtered by campaign. Each must be a campaign of the workspace; null or [] means every campaign.","type":["array","null"],"maxItems":50,"items":{"description":"A campaign.","type":"string","format":"uuid"}}},"required":["url"]},"example":{"url":"https://hooks.example.com/nutalk","events":["call.completed","call.failed"],"description":"Production CRM sync"}}}},"responses":{"201":{"description":"The endpoint, with its secret (shown only now).","content":{"application/json":{"example":{"success":true,"data":{"id":"ff19434c-ce6c-4dfd-a07d-7b367ad4396e","url":"https://hooks.example.com/nutalk","description":"Production CRM sync","events":["call.completed","call.failed"],"campaign_ids":null,"is_active":true,"last_delivery_at":null,"last_delivery_status":null,"created_at":"2026-10-02T03:44:44.387419+00:00","updated_at":"2026-10-02T03:44:44.387419+00:00","secret":"whsec_9c1f…"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The URL does not start with `https://`, or points to localhost or a private address; or `campaign_ids` names a campaign that isn't the workspace's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: `events` names a conversation event and the key lacks `conversations:read`, a call event or `*` (the default) and the key lacks `calls:read`, or a conversion event and the key lacks `conversions:read` (or Conversions is off for the workspace: `details.reason` `conversions_off`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The workspace already has 20 endpoints.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/webhooks/{id}":{"get":{"operationId":"getWebhook","summary":"Get a webhook endpoint","description":"Returns one endpoint (without its secret).\n\nRequires the `webhooks:read` scope.","tags":["Webhooks"],"x-required-scope":"webhooks:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/webhooks#getWebhook"},"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook endpoint id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The endpoint.","content":{"application/json":{"example":{"success":true,"data":{"id":"ff19434c-ce6c-4dfd-a07d-7b367ad4396e","url":"https://hooks.example.com/nutalk","description":"Production CRM sync","events":["call.completed","call.failed"],"campaign_ids":null,"is_active":true,"last_delivery_at":null,"last_delivery_status":null,"created_at":"2026-10-02T03:44:44.387419+00:00","updated_at":"2026-10-02T03:44:44.387419+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No webhook endpoint with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateWebhook","summary":"Update a webhook endpoint","description":"Changes the URL, events or description, or pauses deliveries with `is_active: false`. The secret does not change. Deliveries already queued go to the current URL only while the endpoint still names their event.\n\nRequires the `webhooks:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Webhooks"],"x-required-scope":"webhooks:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/webhooks#updateWebhook"},"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook endpoint id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"description":"New public URL starting with `https://`.","type":"string","format":"uri","maxLength":2000},"events":{"description":"Events to receive. `*` is every call and campaign event; call events (and `*`) carry each call's numbers and outcome, so they take a key that also has `calls:read`. Conversation events (`message.received`, `conversation.status_changed`, `conversation.assigned`) carry customer messages, so they must be named, with a key that has `conversations:read`. Conversion events (`conversion.recorded`, `conversion.retracted`) must be named too, with a key that has `conversions:read`, while Conversions is on for the workspace.","type":"array","minItems":1,"maxItems":16,"items":{"description":"An event name.","type":"string","enum":["*","call.started","call.completed","call.failed","campaign.enrollment.completed","message.received","conversation.status_changed","conversation.assigned","conversion.recorded","conversion.retracted"]}},"description":{"description":"New description, or null.","type":["string","null"],"maxLength":500},"is_active":{"description":"false: stop deliveries; true: resume.","type":"boolean"},"campaign_ids":{"description":"Only these campaigns' call and campaign events (call.started, call.completed, call.failed, campaign.enrollment.completed): a call outside every campaign, or another campaign's, isn't sent. Conversation and conversion events aren't filtered by campaign. Each must be a campaign of the workspace; null or [] means every campaign.","type":["array","null"],"maxItems":50,"items":{"description":"A campaign.","type":"string","format":"uuid"}}}},"example":{"is_active":false}}}},"responses":{"200":{"description":"The updated endpoint.","content":{"application/json":{"example":{"success":true,"data":{"id":"ff19434c-ce6c-4dfd-a07d-7b367ad4396e","url":"https://hooks.example.com/nutalk","description":"Production CRM sync","events":["call.completed","call.failed"],"campaign_ids":null,"is_active":false,"last_delivery_at":null,"last_delivery_status":null,"created_at":"2026-10-02T03:44:44.387419+00:00","updated_at":"2026-10-02T03:44:44.387419+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateWebhook"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The URL does not start with `https://`, or points to localhost or a private address; or `campaign_ids` names a campaign that isn't the workspace's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key lacks `conversations:read` (or `calls:read`, or `conversions:read`) and either `events` names a conversation event (or a call event or `*`, or a conversion event), or the request changes the URL or turns the endpoint back on while its current or new events name one; or the key lacks `calls:read` and the request changes `campaign_ids` while the endpoint's events name a call event or `*`; or `events` names a conversion event while Conversions is off for the workspace (`details.reason` `conversions_off`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No webhook endpoint with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook endpoint","description":"Deletes the endpoint; deliveries already queued for it are dropped.\n\nRequires the `webhooks:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Webhooks"],"x-required-scope":"webhooks:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/webhooks#deleteWebhook"},"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook endpoint id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The endpoint was deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"ff19434c-ce6c-4dfd-a07d-7b367ad4396e","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteWebhook"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No webhook endpoint with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/webhooks/{id}/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"List an endpoint's deliveries","description":"The endpoint's delivery log, newest first: every attempt to deliver an event in the last 30 days, which attempt it was, what the endpoint answered and how long it took. Ids and codes only: never the body sent or the endpoint's answer.\n\nRequires the `webhooks:read` scope.\n\n`outcome` is `delivered` (a 2xx), `failed` (retried, up to 5 attempts) or `blocked` (never retried). `error_code`: `http_status`, `timeout`, `network_error`, `private_address`, `invalid_url`. `reference_id` is what the event reports (the call, the message, the change): one delivery's attempts share it.","tags":["Webhooks"],"x-required-scope":"webhooks:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/webhooks#listWebhookDeliveries"},"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook endpoint id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"event","in":"query","required":false,"description":"Only this event's attempts.","schema":{"type":"string","enum":["call.started","call.completed","call.failed","campaign.enrollment.completed","message.received","conversation.status_changed","conversation.assigned","conversion.recorded","conversion.retracted"]}},{"name":"outcome","in":"query","required":false,"description":"Only attempts that ended this way.","schema":{"type":"string","enum":["delivered","failed","blocked"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of attempts.","content":{"application/json":{"example":{"success":true,"data":[{"id":"9b5d2f7c-4e0a-4b6d-9f8c-3a2e0d9b7f54","event":"conversion.recorded","reference_id":"a8f7e6d5-c4b3-4a29-8817-0f6e5d4c3b2a","attempt":2,"outcome":"delivered","http_status":200,"error_code":null,"duration_ms":184,"attempted_at":"2026-10-25T23:43:12.501+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No webhook endpoint with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/locations":{"get":{"operationId":"listClinicLocations","summary":"List locations","description":"Lists the clinic's locations with their opening hours, by name.\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#listClinicLocations"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only locations in this status. Without it, every location but the archived ones.","schema":{"type":"string","enum":["active","inactive","archived"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of locations.","content":{"application/json":{"example":{"success":true,"data":[{"id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","name":"Clínica Centro","timezone":"America/Monterrey","address":"Av. Constitución 100, Monterrey, N.L.","phone_number":"+528112345678","status":"active","hours":[{"id":"9d3f5b7c-1e2a-4b4d-8f6c-8e0a2c4d6f81","day_of_week":1,"open_time":"09:00","close_time":"18:00","is_active":true}],"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createClinicLocation","summary":"Create a location","description":"Creates a location. A workspace's first location adopts the rooms that have none; `warnings` says when that could not be done.\n\nRequires the `clinic_setup:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#createClinicLocation"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The location's name.","type":"string","minLength":1,"maxLength":200},"timezone":{"description":"IANA time zone of the location's clock (default America/Monterrey).","type":"string","maxLength":64},"address":{"description":"Street address; null clears it.","type":["string","null"],"maxLength":500},"phone_number":{"description":"The location's phone; null clears it.","type":["string","null"],"maxLength":40}},"required":["name"]},"example":{"name":"Clínica Centro","timezone":"America/Monterrey","address":"Av. Constitución 100, Monterrey, N.L.","phone_number":"+528112345678"}}}},"responses":{"201":{"description":"The location.","content":{"application/json":{"example":{"success":true,"data":{"id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","name":"Clínica Centro","timezone":"America/Monterrey","address":"Av. Constitución 100, Monterrey, N.L.","phone_number":"+528112345678","status":"active","hours":[],"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/locations/{id}":{"get":{"operationId":"getClinicLocation","summary":"Get a location","description":"Returns one location (archived ones too) with its opening hours.\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#getClinicLocation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The location id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The location.","content":{"application/json":{"example":{"success":true,"data":{"id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","name":"Clínica Centro","timezone":"America/Monterrey","address":"Av. Constitución 100, Monterrey, N.L.","phone_number":"+528112345678","status":"active","hours":[{"id":"9d3f5b7c-1e2a-4b4d-8f6c-8e0a2c4d6f81","day_of_week":1,"open_time":"09:00","close_time":"18:00","is_active":true}],"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No location with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateClinicLocation","summary":"Update a location","description":"Changes the fields you send.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#updateClinicLocation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The location id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The location's name.","type":"string","minLength":1,"maxLength":200},"timezone":{"description":"IANA time zone of the location's clock.","type":"string","maxLength":64},"address":{"description":"Street address; null clears it.","type":["string","null"],"maxLength":500},"phone_number":{"description":"The location's phone; null clears it.","type":["string","null"],"maxLength":40},"status":{"description":"`inactive` keeps the location out of new bookings; `active` brings it back. Archiving is the DELETE.","type":"string","enum":["active","inactive"]}}},"example":{"phone_number":"+528187654321"}}}},"responses":{"200":{"description":"The updated location.","content":{"application/json":{"example":{"success":true,"data":{"id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","name":"Clínica Centro","timezone":"America/Monterrey","address":"Av. Constitución 100, Monterrey, N.L.","phone_number":"+528187654321","status":"active","hours":[{"id":"9d3f5b7c-1e2a-4b4d-8f6c-8e0a2c4d6f81","day_of_week":1,"open_time":"09:00","close_time":"18:00","is_active":true}],"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicLocation"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No location with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The location is archived (removed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"archiveClinicLocation","summary":"Archive a location","description":"Archives the location (the dashboard's Remove): it leaves every flow and its history stays. Archiving an archived location changes nothing.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#archiveClinicLocation"},"parameters":[{"name":"id","in":"path","required":true,"description":"The location id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The location is archived.","content":{"application/json":{"example":{"success":true,"data":{"id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","archived":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"archiveClinicLocation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No location with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/locations/{id}/hours":{"post":{"operationId":"createClinicLocationHours","summary":"Add opening hours","description":"Adds one opening-hours range to a location. Opening hours bound the services nobody staffs (`staff_involvement: none`); a location without any is open whenever its doctors work. Add one range per weekday (or several, for a break).\n\nRequires the `clinic_setup:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#createClinicLocationHours"},"parameters":[{"name":"id","in":"path","required":true,"description":"The location id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"day_of_week":{"description":"0 = Sunday … 6 = Saturday.","type":"integer","minimum":0,"maximum":6},"open_time":{"description":"HH:MM (24-hour) on the location's clock.","type":"string"},"close_time":{"description":"HH:MM after open_time (23:59 at the latest).","type":"string"}},"required":["day_of_week","open_time","close_time"]},"example":{"day_of_week":1,"open_time":"09:00","close_time":"18:00"}}}},"responses":{"201":{"description":"The range.","content":{"application/json":{"example":{"success":true,"data":{"id":"9d3f5b7c-1e2a-4b4d-8f6c-8e0a2c4d6f81","day_of_week":1,"open_time":"09:00","close_time":"18:00","is_active":true}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No location with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The location is archived (removed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/location-hours/{id}":{"patch":{"operationId":"updateClinicLocationHours","summary":"Update opening hours","description":"Changes one opening-hours range of a location (an id from the location's `hours`).\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#updateClinicLocationHours"},"parameters":[{"name":"id","in":"path","required":true,"description":"The opening-hours range id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"day_of_week":{"description":"0 = Sunday … 6 = Saturday.","type":"integer","minimum":0,"maximum":6},"open_time":{"description":"HH:MM (24-hour) on the location's clock.","type":"string"},"close_time":{"description":"HH:MM after open_time (23:59 at the latest).","type":"string"},"is_active":{"description":"false keeps the range but stops applying it.","type":"boolean"}}},"example":{"close_time":"19:00"}}}},"responses":{"200":{"description":"The updated range.","content":{"application/json":{"example":{"success":true,"data":{"id":"9d3f5b7c-1e2a-4b4d-8f6c-8e0a2c4d6f81","day_of_week":1,"open_time":"09:00","close_time":"19:00","is_active":true}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicLocationHours"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The closing time would not be after the opening time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No opening-hours range with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteClinicLocationHours","summary":"Remove opening hours","description":"Deletes one opening-hours range of a location.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#deleteClinicLocationHours"},"parameters":[{"name":"id","in":"path","required":true,"description":"The opening-hours range id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The range was deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"9d3f5b7c-1e2a-4b4d-8f6c-8e0a2c4d6f81","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteClinicLocationHours"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No opening-hours range with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/rooms":{"get":{"operationId":"listClinicRooms","summary":"List rooms","description":"Lists the clinic's rooms, by name, each with its spots: the places inside a room that hold one appointment at a time (a chair, a bed, a station).\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#listClinicRooms"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"location_id","in":"query","required":false,"description":"Only rooms of this location.","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Only rooms in this status. Without it, every room but the archived ones.","schema":{"type":"string","enum":["active","inactive","archived"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of rooms.","content":{"application/json":{"example":{"success":true,"data":[{"id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","name":"Sala de sueros","location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_type":"iv","capacity":4,"status":"active","spots":[{"id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","name":"Sillón 1","position":1,"status":"active"}],"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createClinicRoom","summary":"Create a room","description":"Creates a room with its first spot, named as the room: appointments are booked into spots. Add more spots for a room that holds several patients at once. A room in an inactive location takes no bookings until its location is active.\n\nRequires the `clinic_setup:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#createClinicRoom"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The room's name (its first spot takes it too).","type":"string","minLength":1,"maxLength":200},"location_id":{"description":"The location the room is in (an active location of yours); null unlinks it.","type":["string","null"],"format":"uuid"},"room_type":{"description":"A label services can require (`required_room_type`), e.g. `iv` or `consultation`.","type":["string","null"],"maxLength":100},"capacity":{"description":"How many people fit (default 1).","type":"integer","minimum":1,"maximum":100}},"required":["name"]},"example":{"name":"Sala de sueros","location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_type":"iv","capacity":4}}}},"responses":{"201":{"description":"The room, with its first spot.","content":{"application/json":{"example":{"success":true,"data":{"id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","name":"Sala de sueros","location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_type":"iv","capacity":4,"status":"active","spots":[{"id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","name":"Sala de sueros","position":1,"status":"active"}],"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `location_id` is not a location of yours, or it is archived (`details.ids`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/rooms/{id}":{"get":{"operationId":"getClinicRoom","summary":"Get a room","description":"Returns one room (archived ones too) with its spots.\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#getClinicRoom"},"parameters":[{"name":"id","in":"path","required":true,"description":"The room id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The room.","content":{"application/json":{"example":{"success":true,"data":{"id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","name":"Sala de sueros","location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_type":"iv","capacity":4,"status":"active","spots":[{"id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","name":"Sillón 1","position":1,"status":"active"}],"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No room with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateClinicRoom","summary":"Update a room","description":"Changes the fields you send.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#updateClinicRoom"},"parameters":[{"name":"id","in":"path","required":true,"description":"The room id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The room's name.","type":"string","minLength":1,"maxLength":200},"location_id":{"description":"The location the room is in (an active location of yours); null unlinks it.","type":["string","null"],"format":"uuid"},"room_type":{"description":"A label services can require (`required_room_type`), e.g. `iv` or `consultation`.","type":["string","null"],"maxLength":100},"capacity":{"description":"How many people fit (default 1).","type":"integer","minimum":1,"maximum":100},"status":{"description":"`inactive` keeps the room out of new bookings; `active` brings it back. Archiving is the DELETE.","type":"string","enum":["active","inactive"]}}},"example":{"capacity":6}}}},"responses":{"200":{"description":"The updated room.","content":{"application/json":{"example":{"success":true,"data":{"id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","name":"Sala de sueros","location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_type":"iv","capacity":6,"status":"active","spots":[{"id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","name":"Sillón 1","position":1,"status":"active"}],"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicRoom"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `location_id` is not a location of yours, or it is archived (`details.ids`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No room with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The room is archived (removed). `validation_error`: The change would make the room active in a location that isn't active, or move an active room into one (`details.reason` `location_closed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"archiveClinicRoom","summary":"Archive a room","description":"Archives the room (the dashboard's Remove). Archiving an archived room changes nothing.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#archiveClinicRoom"},"parameters":[{"name":"id","in":"path","required":true,"description":"The room id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The room is archived.","content":{"application/json":{"example":{"success":true,"data":{"id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","archived":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"archiveClinicRoom"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No room with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/rooms/{id}/spots":{"post":{"operationId":"createClinicSpot","summary":"Add a spot","description":"Adds a spot to a room: one more place that holds one appointment at a time.\n\nRequires the `clinic_setup:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#createClinicSpot"},"parameters":[{"name":"id","in":"path","required":true,"description":"The room id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The spot's name, e.g. `Sillón 2`.","type":"string","minLength":1,"maxLength":200},"position":{"description":"Its order in the room (default 1).","type":"integer","minimum":1,"maximum":200}},"required":["name"]},"example":{"name":"Sillón 2","position":2}}}},"responses":{"201":{"description":"The spot.","content":{"application/json":{"example":{"success":true,"data":{"id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","name":"Sillón 2","position":2,"status":"active","created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No room with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The room is archived (removed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/spots/{id}":{"patch":{"operationId":"updateClinicSpot","summary":"Update a spot","description":"Renames, reorders or switches off one spot (an id from its room's `spots`).\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#updateClinicSpot"},"parameters":[{"name":"id","in":"path","required":true,"description":"The spot id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The spot's name, e.g. `Sillón 2`.","type":"string","minLength":1,"maxLength":200},"position":{"description":"Its order in the room (default 1).","type":"integer","minimum":1,"maximum":200},"status":{"description":"`inactive` keeps the spot out of new bookings; `active` brings it back. Archiving is the DELETE.","type":"string","enum":["active","inactive"]}}},"example":{"status":"inactive"}}}},"responses":{"200":{"description":"The updated spot.","content":{"application/json":{"example":{"success":true,"data":{"id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","name":"Sillón 2","position":2,"status":"inactive","created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicSpot"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No spot with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The spot is archived (removed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"archiveClinicSpot","summary":"Archive a spot","description":"Archives one spot. Archiving an archived spot changes nothing.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#archiveClinicSpot"},"parameters":[{"name":"id","in":"path","required":true,"description":"The spot id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The spot is archived.","content":{"application/json":{"example":{"success":true,"data":{"id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","archived":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"archiveClinicSpot"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No spot with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/doctors":{"get":{"operationId":"listClinicDoctors","summary":"List doctors","description":"Lists the people who take clinic appointments, by name: doctors, nurses, therapists, technicians and other staff (their `role`).\n\nRequires the `clinic_setup:read` scope.\n\nA removed doctor reads `status: \"archived\"`: listed only with that filter, kept so past appointments keep the name.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#listClinicDoctors"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only doctors in this status. Without it, every doctor but the archived ones.","schema":{"type":"string","enum":["active","inactive","archived"]}},{"name":"role","in":"query","required":false,"description":"Only this role.","schema":{"type":"string","enum":["doctor","nurse","therapist","technician","staff"]}},{"name":"location_id","in":"query","required":false,"description":"Only doctors whose default location is this one.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of doctors.","content":{"application/json":{"example":{"success":true,"data":[{"id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","name":"Dra. Ana López","role":"doctor","specialty":"Dermatología","email":"ana.lopez@clinica.mx","phone_number":"+528112345679","timezone":"America/Monterrey","status":"active","default_location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","default_room_id":null,"max_concurrent_supervised":3,"user_id":null,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createClinicDoctor","summary":"Add a doctor","description":"Adds a doctor (or nurse, therapist…) to Clinic Scheduling. They take appointments once they have weekly hours (PUT /clinic/doctors/{id}/weekly-hours) and a service allows them.\n\nRequires the `clinic_setup:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#createClinicDoctor"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"How patients and the agents name them.","type":"string","minLength":1,"maxLength":200},"role":{"description":"What they do (default `doctor`): services can require a role.","type":"string","enum":["doctor","nurse","therapist","technician","staff"]},"specialty":{"description":"Shown to patients and the agents.","type":["string","null"],"maxLength":200},"email":{"description":"Contact email.","type":["string","null"],"format":"email","maxLength":320},"phone_number":{"description":"Contact phone.","type":["string","null"],"maxLength":40},"timezone":{"description":"IANA time zone of their clock. Without it, their clinic's (their location's, else the workspace's).","type":"string","maxLength":64},"default_location_id":{"description":"Where they usually work (an active location of yours).","type":["string","null"],"format":"uuid"},"default_room_id":{"description":"The room they usually use (an active room of yours).","type":["string","null"],"format":"uuid"},"max_concurrent_supervised":{"description":"How many `supervised` services they can watch at once (default 3).","type":"integer","minimum":1,"maximum":50},"user_id":{"description":"The workspace member who is this doctor (their My Schedule shows these appointments); null unlinks.","type":["string","null"],"format":"uuid"}},"required":["name"]},"example":{"name":"Dra. Ana López","role":"doctor","specialty":"Dermatología","email":"ana.lopez@clinica.mx","default_location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70"}}}},"responses":{"201":{"description":"The doctor.","content":{"application/json":{"example":{"success":true,"data":{"id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","name":"Dra. Ana López","role":"doctor","specialty":"Dermatología","email":"ana.lopez@clinica.mx","phone_number":"+528112345679","timezone":"America/Monterrey","status":"active","default_location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","default_room_id":null,"max_concurrent_supervised":3,"user_id":null,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `default_location_id` or `default_room_id` is not an active location or room of yours, or `user_id` is not a member of the workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/doctors/{id}":{"get":{"operationId":"getClinicDoctor","summary":"Get a doctor","description":"Returns one doctor (removed ones too).\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#getClinicDoctor"},"parameters":[{"name":"id","in":"path","required":true,"description":"The doctor id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The doctor.","content":{"application/json":{"example":{"success":true,"data":{"id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","name":"Dra. Ana López","role":"doctor","specialty":"Dermatología","email":"ana.lopez@clinica.mx","phone_number":"+528112345679","timezone":"America/Monterrey","status":"active","default_location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","default_room_id":null,"max_concurrent_supervised":3,"user_id":null,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No doctor with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateClinicDoctor","summary":"Update a doctor","description":"Changes the fields you send. `inactive` stops new bookings and keeps their appointments; removing them is the DELETE. `warnings` lists the dates whose day changes a new time zone could not rebuild.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#updateClinicDoctor"},"parameters":[{"name":"id","in":"path","required":true,"description":"The doctor id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"How patients and the agents name them.","type":"string","minLength":1,"maxLength":200},"role":{"description":"What they do: services can require a role.","type":"string","enum":["doctor","nurse","therapist","technician","staff"]},"specialty":{"description":"Shown to patients and the agents.","type":["string","null"],"maxLength":200},"email":{"description":"Contact email.","type":["string","null"],"format":"email","maxLength":320},"phone_number":{"description":"Contact phone.","type":["string","null"],"maxLength":40},"timezone":{"description":"IANA time zone of their clock. A new zone moves their weekly hours with it (same wall times) and rebuilds their day changes.","type":"string","maxLength":64},"default_location_id":{"description":"Where they usually work (an active location of yours).","type":["string","null"],"format":"uuid"},"default_room_id":{"description":"The room they usually use (an active room of yours).","type":["string","null"],"format":"uuid"},"max_concurrent_supervised":{"description":"How many `supervised` services they can watch at once (default 3).","type":"integer","minimum":1,"maximum":50},"user_id":{"description":"The workspace member who is this doctor (their My Schedule shows these appointments); null unlinks.","type":["string","null"],"format":"uuid"},"status":{"description":"`inactive` keeps the doctor out of new bookings; `active` brings it back. Archiving is the DELETE.","type":"string","enum":["active","inactive"]}}},"example":{"specialty":"Dermatología pediátrica"}}}},"responses":{"200":{"description":"The updated doctor.","content":{"application/json":{"example":{"success":true,"data":{"id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","name":"Dra. Ana López","role":"doctor","specialty":"Dermatología pediátrica","email":"ana.lopez@clinica.mx","phone_number":"+528112345679","timezone":"America/Monterrey","status":"active","default_location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","default_room_id":null,"max_concurrent_supervised":3,"user_id":null,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicDoctor"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A location, room or member named is not yours, or `timezone` is not an IANA time zone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No doctor with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The doctor was removed, or a new `user_id` while their calendar accounts are connected (disconnect them first).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"removeClinicDoctor","summary":"Remove a doctor","description":"Removes the doctor (the dashboard's Remove): refused while they have an upcoming appointment (move or cancel it, or set them `inactive` instead). Their calendar links, calendar subscriptions and round-robin places go; the record stays archived so past appointments keep the name.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#removeClinicDoctor"},"parameters":[{"name":"id","in":"path","required":true,"description":"The doctor id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The doctor is removed (`already_removed` when they were).","content":{"application/json":{"example":{"success":true,"data":{"id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","removed":true,"already_removed":false,"past_appointments_kept":12}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"removeClinicDoctor"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No doctor with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: They have upcoming appointments (`details.reason` is `PROVIDER_HAS_UPCOMING_APPOINTMENTS`, `details.upcoming_appointments` the count).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/doctors/{id}/weekly-hours":{"get":{"operationId":"getClinicDoctorWeeklyHours","summary":"Get a doctor's weekly hours","description":"Returns the doctor's recurring week: `hours`, the every-week ranges on their own clock that PUT replaces, and `other_hours`, the rows a PUT keeps as they are (alternate weeks, hours bounded to dates, hours on another clock; edited in the dashboard).\n\nRequires the `clinic_setup:read` scope.\n\n`other_hours` rows also carry `timezone`, `every_weeks` with `anchor_date` (every other week, …) and `effective_start`/`effective_end` (dates the row applies between).\n\nDays off and day changes are not part of the week: see the doctor's days off.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#getClinicDoctorWeeklyHours"},"parameters":[{"name":"id","in":"path","required":true,"description":"The doctor id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The doctor's week. Times are HH:MM on `timezone`; `day_of_week` 0 is Sunday.","content":{"application/json":{"example":{"success":true,"data":{"doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","timezone":"America/Monterrey","hours":[{"id":"e28ea0c1-6d7f-4a9c-9ebc-3d5f7b9cbed6","day_of_week":1,"start":"09:00","end":"14:00","location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":null,"slot_interval_minutes":15}],"other_hours":[]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No doctor with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"setClinicDoctorWeeklyHours","summary":"Set a doctor's weekly hours","description":"Replaces the doctor's every-week hours with `hours`: the whole week, so a weekday left out has no hours and [] clears the week. It runs through the dashboard's weekly-hours writer: ranges of one day can't overlap, `other_hours` are kept, the day changes of the weekdays that change are rebuilt, and the appointments the new week leaves outside its hours are listed (never cancelled: reception gives them a new time).\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.\n\nThe hours are saved on the doctor's clock: a later time zone change moves them with it.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#setClinicDoctorWeeklyHours"},"parameters":[{"name":"id","in":"path","required":true,"description":"The doctor id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"hours":{"description":"The week's ranges.","type":"array","maxItems":56,"items":{"description":"A range.","type":"object","properties":{"day_of_week":{"description":"0 = Sunday … 6 = Saturday.","type":"integer","minimum":0,"maximum":6},"start":{"description":"HH:MM (24-hour) on the doctor's clock.","type":"string"},"end":{"description":"HH:MM after start (23:59 at the latest).","type":"string"},"id":{"description":"The range of GET it replaces: it keeps that range's location, room and slot step. A new range takes its weekday's first range's, else the doctor's default location.","type":"string","format":"uuid"}},"required":["day_of_week","start","end"]}},"dry_run":{"description":"true: saves nothing and lists the appointments the new week would leave outside.","type":"boolean","default":false}},"required":["hours"]},"example":{"hours":[{"day_of_week":1,"start":"09:00","end":"14:00"},{"day_of_week":1,"start":"16:00","end":"19:00"},{"day_of_week":3,"start":"09:00","end":"14:00"}]}}}},"responses":{"200":{"description":"The outcome: `conflicts` (the first 200) and `conflict_count` count the appointments left outside the new hours (`patient_name` and `service` are null unless the key holds `appointments:read`); `recompile_warnings` names the dates whose day changes could not be rebuilt (`recompile_failed` when the rebuild failed altogether: open those days in the dashboard); `weekly_hours` is the saved week (null on a dry run).","content":{"application/json":{"example":{"success":true,"data":{"dry_run":false,"conflicts":[{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","starts_at":"2026-10-19T22:00:00.000Z","ends_at":"2026-10-19T22:30:00.000Z","timezone":"America/Monterrey","patient_name":"María Pérez","service":"Consulta dermatológica"}],"conflict_count":1,"recompile_warnings":[],"recompile_failed":false,"weekly_hours":{"doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","timezone":"America/Monterrey","hours":[{"id":"e28ea0c1-6d7f-4a9c-9ebc-3d5f7b9cbed6","day_of_week":1,"start":"09:00","end":"14:00","location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":null,"slot_interval_minutes":15}],"other_hours":[]}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setClinicDoctorWeeklyHours"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: Two ranges of a day overlap (`details.reason` `WEEKLY_OVERLAP`, `details.day_of_week`), or a day mixes two time zones (`WEEKLY_TIMEZONE_MIX`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No doctor with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The doctor was removed, has too many schedule rows, or the weekdays changed have more than 25 day changes ahead (`WEEKLY_TOO_MANY_DAY_CHANGES`: change fewer weekdays at a time).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/doctors/{id}/days-off":{"get":{"operationId":"listClinicDoctorDaysOff","summary":"List a doctor's days off","description":"Lists the doctor's blocked time (days off, time off), soonest first: by default what has not ended yet.\n\nRequires the `clinic_setup:read` scope.\n\nDay changes the clinic made to the doctor's hours for one date are not days off and are left out.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#listClinicDoctorDaysOff"},"parameters":[{"name":"id","in":"path","required":true,"description":"The doctor id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"from","in":"query","required":false,"description":"Only blocks that end after this time (default: now).","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"Only blocks that start before this time.","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of blocked times.","content":{"application/json":{"example":{"success":true,"data":[{"id":"f39fb1d2-7e8a-4bad-8fcd-4e6a8cadcfe7","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","starts_at":"2026-12-24T06:00:00+00:00","ends_at":"2026-12-26T06:00:00+00:00","reason":"Vacaciones","created_at":"2026-10-12T16:02:11.402+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No doctor with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createClinicDoctorDayOff","summary":"Block a doctor's time","description":"Blocks the doctor's time (a day off, a vacation, an afternoon) through the database's one write path for it: it joins the blocks it touches, refuses time that already ended, and lists the appointments inside. Those stay booked: reception moves them. Send the time as instants (`starts_at`, `ends_at`), as part of one day on the doctor's clock (`date`, `from`, `to`), or as whole days on the doctor's clock (`first_date`, `last_date`).\n\nRequires the `clinic_setup:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#createClinicDoctorDayOff"},"parameters":[{"name":"id","in":"path","required":true,"description":"The doctor id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"starts_at":{"description":"With ends_at: the block's start.","type":"string","format":"date-time"},"ends_at":{"description":"With starts_at: the block's end.","type":"string","format":"date-time"},"date":{"description":"With from and to: the day (YYYY-MM-DD) on the doctor's clock.","type":"string"},"from":{"description":"HH:MM the block starts that day.","type":"string"},"to":{"description":"HH:MM the block ends that day.","type":"string"},"first_date":{"description":"With last_date: the first whole day off (YYYY-MM-DD).","type":"string"},"last_date":{"description":"The last whole day off, included.","type":"string"},"reason":{"description":"Why (shown to the team).","type":["string","null"],"maxLength":500},"dry_run":{"description":"true: saves nothing; lists what the block would cover.","type":"boolean","default":false},"conflicts_key":{"description":"The `conflicts_key` of a dry run: the block is made only while the same appointments are inside (409 otherwise).","type":"string"}}},"example":{"first_date":"2026-12-24","last_date":"2026-12-25","reason":"Vacaciones"}}}},"responses":{"201":{"description":"The block was made (200 for a dry run). `day_off` is the stored block (null on a dry run), `starts_at`/`ends_at` its range after joining, `joined` the blocks it absorbed, `conflicts` (the first 200) and `conflict_count` the appointments inside (`patient_name` and `service` are null unless the key holds `appointments:read`), `conflicts_key` names that set.","content":{"application/json":{"example":{"success":true,"data":{"dry_run":false,"day_off":{"id":"f39fb1d2-7e8a-4bad-8fcd-4e6a8cadcfe7","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","starts_at":"2026-12-24T06:00:00+00:00","ends_at":"2026-12-26T06:00:00+00:00","reason":"Vacaciones","created_at":"2026-10-12T16:02:11.402+00:00"},"starts_at":"2026-12-24T06:00:00+00:00","ends_at":"2026-12-26T06:00:00+00:00","joined":[],"conflicts":[],"conflict_count":0,"conflicts_key":"00000000000000000000000000000abc"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The time already ended (`details.reason` `CLINIC_BLOCK_IN_PAST`) or is too long (`CLINIC_BLOCK_TOO_LONG`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No doctor with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The doctor was removed, or the appointments inside changed since the dry run whose `conflicts_key` was sent (`CLINIC_BLOCK_APPOINTMENTS_CHANGED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/days-off/{id}":{"delete":{"operationId":"deleteClinicDayOff","summary":"Remove a day off","description":"Removes a doctor's blocked time (an id from their days off): the time is open for booking again at once.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#deleteClinicDayOff"},"parameters":[{"name":"id","in":"path","required":true,"description":"The day off id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The block was removed.","content":{"application/json":{"example":{"success":true,"data":{"id":"f39fb1d2-7e8a-4bad-8fcd-4e6a8cadcfe7","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteClinicDayOff"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No day off with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: Another block joined into it meanwhile (`details.reason` `CLINIC_BLOCK_CHANGED`): read the doctor's days off again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/services":{"get":{"operationId":"listClinicServices","summary":"List services","description":"Lists the services patients book (appointment types) in the clinic's display order, with the rules the scheduling engine applies and the facts the agents answer from.\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#listClinicServices"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only services in this status. Without it, every service but the archived ones.","schema":{"type":"string","enum":["active","inactive","archived"]}},{"name":"category","in":"query","required":false,"description":"Only services of this category (exact).","schema":{"type":"string"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of services.","content":{"application/json":{"example":{"success":true,"data":[{"id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","name":"Consulta dermatológica","description":"Valoración de piel, cabello y uñas.","category":"Dermatología","status":"active","duration_minutes":30,"buffer_before_minutes":0,"buffer_after_minutes":10,"color":"#0EA5E9","default_room_id":null,"required_room_type":null,"eligible_spot_ids":[],"staff_involvement":"exclusive","required_staff_role":"doctor","allowed_doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"],"min_notice_minutes":60,"max_advance_days":90,"full_hour_start":false,"requires_human_confirmation":false,"currency":"MXN","price":900,"price_note":null,"search_aliases":["consulta de piel","dermatólogo"],"benefits":[],"indications":[],"contraindications":[],"preparation_instructions":"Llegar sin maquillaje.","aftercare_instructions":null,"patient_arrival_offset_minutes":10,"intake_form_required":false,"intake_form_url":null,"intake_form_instructions":null,"handoff_summary":null,"modality":null,"mechanism_summary":null,"ideal_candidate":null,"operational_notes":null,"service_details":[],"information_sources":[],"content_review_status":"tenant_confirmed","display_order":100,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createClinicService","summary":"Create a service","description":"Creates a service. Every field but `name` is optional.\n\nRequires the `clinic_setup:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#createClinicService"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"What patients book, e.g. `Consulta dermatológica`.","type":"string","minLength":1,"maxLength":200},"description":{"description":"What it is; the agents read it.","type":["string","null"],"maxLength":5000},"category":{"description":"A label to group services.","type":["string","null"],"maxLength":200},"duration_minutes":{"description":"How long it takes (default 30).","type":"integer","minimum":5,"maximum":480},"buffer_before_minutes":{"description":"Time kept free before it.","type":"integer","minimum":0,"maximum":240},"buffer_after_minutes":{"description":"Time kept free after it.","type":"integer","minimum":0,"maximum":240},"color":{"description":"Calendar color, `#RRGGBB` (null: the default).","type":["string","null"]},"default_room_id":{"description":"The room it is usually booked in.","type":["string","null"],"format":"uuid"},"required_room_type":{"description":"Only rooms with this `room_type` can hold it.","type":["string","null"],"maxLength":100},"eligible_spot_ids":{"description":"The spots it can be booked in, the main one first (active spots of yours). Replaces the stored list; [] removes the restriction.","type":"array","maxItems":100,"items":{"description":"A spot.","type":"string","format":"uuid"}},"staff_involvement":{"description":"`exclusive` (default): the doctor is busy for the whole service; `supervised`: one doctor watches several at once (up to their `max_concurrent_supervised`); `none`: nobody staffs it (bounded by the location's opening hours).","type":"string","enum":["exclusive","supervised","none"]},"required_staff_role":{"description":"Only doctors with this role can take it.","type":["string","null"],"enum":["doctor","nurse","therapist","technician","staff",null]},"allowed_doctor_ids":{"description":"Only these doctors can take it (doctors of yours). Replaces the stored list; [] lets every qualified doctor take it.","type":"array","maxItems":200,"items":{"description":"A doctor.","type":"string","format":"uuid"}},"min_notice_minutes":{"description":"How far ahead it must be booked (default 60).","type":"integer","minimum":0,"maximum":20160},"max_advance_days":{"description":"How far into the future it can be booked (default 90).","type":"integer","minimum":1,"maximum":365},"full_hour_start":{"description":"Starts only on the hour.","type":"boolean"},"requires_human_confirmation":{"description":"The agents don't book it on their own: reception or an outside provider confirms first.","type":"boolean"},"currency":{"description":"ISO 4217 code of the price (default MXN).","type":"string","minLength":3,"maxLength":3},"price":{"description":"The regular price, in `currency`; null: no price.","type":["number","null"],"minimum":0},"price_note":{"description":"What the price includes or depends on.","type":["string","null"],"maxLength":1000},"search_aliases":{"description":"Other names patients use for it (the agents match them). Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"benefits":{"description":"Benefits the agents can mention. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"indications":{"description":"Who it is for. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"contraindications":{"description":"Who should not take it. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"preparation_instructions":{"description":"What the patient does before coming.","type":["string","null"],"maxLength":5000},"aftercare_instructions":{"description":"What the patient does afterwards.","type":["string","null"],"maxLength":5000},"patient_arrival_offset_minutes":{"description":"How many minutes early the patient should arrive.","type":["integer","null"],"minimum":0,"maximum":240},"intake_form_required":{"description":"The patient fills a form before the visit.","type":"boolean"},"intake_form_url":{"description":"The form's link.","type":["string","null"],"format":"uri","maxLength":2000},"intake_form_instructions":{"description":"How to fill it.","type":["string","null"],"maxLength":5000},"handoff_summary":{"description":"What the agents tell reception when they hand a patient over.","type":["string","null"],"maxLength":5000},"modality":{"description":"How it is given (e.g. in person, IV).","type":["string","null"],"maxLength":200},"mechanism_summary":{"description":"How it works, in plain words.","type":["string","null"],"maxLength":5000},"ideal_candidate":{"description":"Who benefits most.","type":["string","null"],"maxLength":5000},"operational_notes":{"description":"Notes for the team.","type":["string","null"],"maxLength":5000},"service_details":{"description":"Further facts the agents can share. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"information_sources":{"description":"Where the facts above come from. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"content_review_status":{"description":"How far the patient-facing content was reviewed (default `draft`).","type":"string","enum":["draft","source_backed","tenant_confirmed","medical_review_required","medical_approved"]},"display_order":{"description":"Lower comes first in lists (default 100).","type":"integer","minimum":0,"maximum":10000}},"required":["name"]},"example":{"name":"Consulta dermatológica","duration_minutes":30,"buffer_after_minutes":10,"required_staff_role":"doctor","allowed_doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"],"currency":"MXN","price":900,"search_aliases":["consulta de piel","dermatólogo"]}}}},"responses":{"201":{"description":"The service.","content":{"application/json":{"example":{"success":true,"data":{"id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","name":"Consulta dermatológica","description":"Valoración de piel, cabello y uñas.","category":"Dermatología","status":"active","duration_minutes":30,"buffer_before_minutes":0,"buffer_after_minutes":10,"color":"#0EA5E9","default_room_id":null,"required_room_type":null,"eligible_spot_ids":[],"staff_involvement":"exclusive","required_staff_role":"doctor","allowed_doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"],"min_notice_minutes":60,"max_advance_days":90,"full_hour_start":false,"requires_human_confirmation":false,"currency":"MXN","price":900,"price_note":null,"search_aliases":["consulta de piel","dermatólogo"],"benefits":[],"indications":[],"contraindications":[],"preparation_instructions":"Llegar sin maquillaje.","aftercare_instructions":null,"patient_arrival_offset_minutes":10,"intake_form_required":false,"intake_form_url":null,"intake_form_instructions":null,"handoff_summary":null,"modality":null,"mechanism_summary":null,"ideal_candidate":null,"operational_notes":null,"service_details":[],"information_sources":[],"content_review_status":"tenant_confirmed","display_order":100,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A room, spot or doctor named is not an active one of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/services/{id}":{"get":{"operationId":"getClinicService","summary":"Get a service","description":"Returns one service (archived ones too).\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#getClinicService"},"parameters":[{"name":"id","in":"path","required":true,"description":"The service id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The service.","content":{"application/json":{"example":{"success":true,"data":{"id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","name":"Consulta dermatológica","description":"Valoración de piel, cabello y uñas.","category":"Dermatología","status":"active","duration_minutes":30,"buffer_before_minutes":0,"buffer_after_minutes":10,"color":"#0EA5E9","default_room_id":null,"required_room_type":null,"eligible_spot_ids":[],"staff_involvement":"exclusive","required_staff_role":"doctor","allowed_doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"],"min_notice_minutes":60,"max_advance_days":90,"full_hour_start":false,"requires_human_confirmation":false,"currency":"MXN","price":900,"price_note":null,"search_aliases":["consulta de piel","dermatólogo"],"benefits":[],"indications":[],"contraindications":[],"preparation_instructions":"Llegar sin maquillaje.","aftercare_instructions":null,"patient_arrival_offset_minutes":10,"intake_form_required":false,"intake_form_url":null,"intake_form_instructions":null,"handoff_summary":null,"modality":null,"mechanism_summary":null,"ideal_candidate":null,"operational_notes":null,"service_details":[],"information_sources":[],"content_review_status":"tenant_confirmed","display_order":100,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No service with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateClinicService","summary":"Update a service","description":"Changes the fields you send; a list replaces the stored list. Booked appointments keep their time.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#updateClinicService"},"parameters":[{"name":"id","in":"path","required":true,"description":"The service id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"What patients book, e.g. `Consulta dermatológica`.","type":"string","minLength":1,"maxLength":200},"description":{"description":"What it is; the agents read it.","type":["string","null"],"maxLength":5000},"category":{"description":"A label to group services.","type":["string","null"],"maxLength":200},"duration_minutes":{"description":"How long it takes.","type":"integer","minimum":5,"maximum":480},"buffer_before_minutes":{"description":"Time kept free before it.","type":"integer","minimum":0,"maximum":240},"buffer_after_minutes":{"description":"Time kept free after it.","type":"integer","minimum":0,"maximum":240},"color":{"description":"Calendar color, `#RRGGBB` (null: the default).","type":["string","null"]},"default_room_id":{"description":"The room it is usually booked in.","type":["string","null"],"format":"uuid"},"required_room_type":{"description":"Only rooms with this `room_type` can hold it.","type":["string","null"],"maxLength":100},"eligible_spot_ids":{"description":"The spots it can be booked in, the main one first (active spots of yours). Replaces the stored list; [] removes the restriction.","type":"array","maxItems":100,"items":{"description":"A spot.","type":"string","format":"uuid"}},"staff_involvement":{"description":"`exclusive` (default): the doctor is busy for the whole service; `supervised`: one doctor watches several at once (up to their `max_concurrent_supervised`); `none`: nobody staffs it (bounded by the location's opening hours).","type":"string","enum":["exclusive","supervised","none"]},"required_staff_role":{"description":"Only doctors with this role can take it.","type":["string","null"],"enum":["doctor","nurse","therapist","technician","staff",null]},"allowed_doctor_ids":{"description":"Only these doctors can take it (doctors of yours). Replaces the stored list; [] lets every qualified doctor take it.","type":"array","maxItems":200,"items":{"description":"A doctor.","type":"string","format":"uuid"}},"min_notice_minutes":{"description":"How far ahead it must be booked (default 60).","type":"integer","minimum":0,"maximum":20160},"max_advance_days":{"description":"How far into the future it can be booked (default 90).","type":"integer","minimum":1,"maximum":365},"full_hour_start":{"description":"Starts only on the hour.","type":"boolean"},"requires_human_confirmation":{"description":"The agents don't book it on their own: reception or an outside provider confirms first.","type":"boolean"},"currency":{"description":"ISO 4217 code of the price (default MXN).","type":"string","minLength":3,"maxLength":3},"price":{"description":"The regular price, in `currency`; null: no price.","type":["number","null"],"minimum":0},"price_note":{"description":"What the price includes or depends on.","type":["string","null"],"maxLength":1000},"search_aliases":{"description":"Other names patients use for it (the agents match them). Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"benefits":{"description":"Benefits the agents can mention. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"indications":{"description":"Who it is for. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"contraindications":{"description":"Who should not take it. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"preparation_instructions":{"description":"What the patient does before coming.","type":["string","null"],"maxLength":5000},"aftercare_instructions":{"description":"What the patient does afterwards.","type":["string","null"],"maxLength":5000},"patient_arrival_offset_minutes":{"description":"How many minutes early the patient should arrive.","type":["integer","null"],"minimum":0,"maximum":240},"intake_form_required":{"description":"The patient fills a form before the visit.","type":"boolean"},"intake_form_url":{"description":"The form's link.","type":["string","null"],"format":"uri","maxLength":2000},"intake_form_instructions":{"description":"How to fill it.","type":["string","null"],"maxLength":5000},"handoff_summary":{"description":"What the agents tell reception when they hand a patient over.","type":["string","null"],"maxLength":5000},"modality":{"description":"How it is given (e.g. in person, IV).","type":["string","null"],"maxLength":200},"mechanism_summary":{"description":"How it works, in plain words.","type":["string","null"],"maxLength":5000},"ideal_candidate":{"description":"Who benefits most.","type":["string","null"],"maxLength":5000},"operational_notes":{"description":"Notes for the team.","type":["string","null"],"maxLength":5000},"service_details":{"description":"Further facts the agents can share. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"information_sources":{"description":"Where the facts above come from. Replaces the stored list.","type":"array","maxItems":50,"items":{"description":"One entry.","type":"string"}},"content_review_status":{"description":"How far the patient-facing content was reviewed (default `draft`).","type":"string","enum":["draft","source_backed","tenant_confirmed","medical_review_required","medical_approved"]},"display_order":{"description":"Lower comes first in lists (default 100).","type":"integer","minimum":0,"maximum":10000},"status":{"description":"`inactive` keeps the service out of new bookings; `active` brings it back. Archiving is the DELETE.","type":"string","enum":["active","inactive"]}}},"example":{"price":950,"price_note":"Incluye revisión con dermatoscopio."}}}},"responses":{"200":{"description":"The updated service.","content":{"application/json":{"example":{"success":true,"data":{"id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","name":"Consulta dermatológica","description":"Valoración de piel, cabello y uñas.","category":"Dermatología","status":"active","duration_minutes":30,"buffer_before_minutes":0,"buffer_after_minutes":10,"color":"#0EA5E9","default_room_id":null,"required_room_type":null,"eligible_spot_ids":[],"staff_involvement":"exclusive","required_staff_role":"doctor","allowed_doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"],"min_notice_minutes":60,"max_advance_days":90,"full_hour_start":false,"requires_human_confirmation":false,"currency":"MXN","price":950,"price_note":"Incluye revisión con dermatoscopio.","search_aliases":["consulta de piel","dermatólogo"],"benefits":[],"indications":[],"contraindications":[],"preparation_instructions":"Llegar sin maquillaje.","aftercare_instructions":null,"patient_arrival_offset_minutes":10,"intake_form_required":false,"intake_form_url":null,"intake_form_instructions":null,"handoff_summary":null,"modality":null,"mechanism_summary":null,"ideal_candidate":null,"operational_notes":null,"service_details":[],"information_sources":[],"content_review_status":"tenant_confirmed","display_order":100,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicService"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A room, spot or doctor named is not an active one of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No service with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The service is archived (removed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"archiveClinicService","summary":"Archive a service","description":"Archives the service: it can no longer be booked; booked appointments and history keep it. Archiving an archived service changes nothing.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#archiveClinicService"},"parameters":[{"name":"id","in":"path","required":true,"description":"The service id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The service is archived.","content":{"application/json":{"example":{"success":true,"data":{"id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","archived":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"archiveClinicService"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No service with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/record-fields":{"get":{"operationId":"listClinicRecordFields","summary":"List the patient record's questions","description":"Lists what the clinic wants to know about each patient, in the clinic's order. Only the questions: answers aren't here. Archived questions only with `archived=true`.\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#listClinicRecordFields"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"archived","in":"query","required":false,"description":"`true` lists the archived questions instead.","schema":{"type":"boolean"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of questions.","content":{"application/json":{"example":{"success":true,"data":[{"id":"c06c8eaf-4b5d-4e70-9c9f-1b3d5f708cb4","label":"Municipio","question_es":"¿En qué municipio vives?","question_en":"What city do you live in?","answer_type":"text","options":null,"scope":"patient","needed":true,"all_services":true,"service_ids":[],"contact_field_id":"5a7c9e1b-3d5f-4a7b-9c1d-3e5f7a9b1c2d","position":0,"archived":false,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createClinicRecordField","summary":"Add a question","description":"Adds a question to the clinic's patient record, up to 12 active. A question about the patient creates its contact field, or reuses one with the same name that can hold the answers, in the same save.\n\nRequires the `clinic_setup:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#createClinicRecordField"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"description":"The name on the appointment. A question about the patient names its contact field with it.","type":"string","minLength":1,"maxLength":80},"question_es":{"description":"The question in Spanish. The AI asks in its own words.","type":["string","null"],"maxLength":300},"question_en":{"description":"The question in English.","type":["string","null"],"maxLength":300},"answer_type":{"description":"What the answer is (default `text`).","type":"string","enum":["text","long_text","date","choice","yes_no","number"]},"options":{"description":"The choices of a `choice` question. A `yes_no` question has Sí and No unless you name two others.","type":["array","null"],"minItems":1,"maxItems":30,"items":{"description":"One choice.","type":"string","minLength":1,"maxLength":80}},"scope":{"description":"`patient` asks once and saves on a contact field, which the save creates or reuses. `visit` asks every appointment and saves on it. Default `patient`.","type":"string","enum":["patient","visit"]},"needed":{"description":"Needed before the visit: a gap shows as missing and is asked once more. It never blocks a booking (default false).","type":"boolean"},"all_services":{"description":"Asked for every service (default true).","type":"boolean"},"service_ids":{"description":"Asked only for these services: naming them turns `all_services` off. An empty list never means every service.","type":"array","maxItems":500,"items":{"description":"A service's id.","type":"string","format":"uuid"}}},"required":["label"]},"example":{"label":"Municipio","question_es":"¿En qué municipio vives?","question_en":"What city do you live in?","needed":true}}}},"responses":{"201":{"description":"The question.","content":{"application/json":{"example":{"success":true,"data":{"id":"c06c8eaf-4b5d-4e70-9c9f-1b3d5f708cb4","label":"Municipio","question_es":"¿En qué municipio vives?","question_en":"What city do you live in?","answer_type":"text","options":null,"scope":"patient","needed":true,"all_services":true,"service_ids":[],"contact_field_id":"5a7c9e1b-3d5f-4a7b-9c1d-3e5f7a9b1c2d","position":0,"archived":false,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A rule of the question: `details.reason` says which (`RECORD_FIELD_INVALID`, `RECORD_FIELD_LABEL_UNUSABLE`, `RECORD_FIELD_UNKNOWN_SERVICE`). `service_ids` names something that is not yours (`details.ids`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `details.reason`: `RECORD_FIELD_LIMIT` (12 active questions), `RECORD_FIELD_IN_USE` (its answer type or scope can't change once it holds answers), `RECORD_FIELD_ARCHIVED` (bring it back first), or the contact field its name lands on can't take it: `RECORD_FIELD_NAME_ARCHIVED`, `RECORD_FIELD_NAME_CONFLICT`, `RECORD_FIELD_NAME_TAKEN`, `RECORD_FIELD_CONTACT_FIELDS_LIMIT`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/record-fields/order":{"put":{"operationId":"orderClinicRecordFields","summary":"Order the questions","description":"Sets the list's order: the questions you name, in that order, and any other after them in its order. Returns the active questions as they now stand.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#orderClinicRecordFields"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"field_ids":{"description":"The questions, each once, first to last.","type":"array","minItems":1,"maxItems":100,"items":{"description":"A question's id.","type":"string","format":"uuid"}}},"required":["field_ids"]},"example":{"field_ids":["c06c8eaf-4b5d-4e70-9c9f-1b3d5f708cb4"]}}}},"responses":{"200":{"description":"The active questions in their new order.","content":{"application/json":{"example":{"success":true,"data":[{"id":"c06c8eaf-4b5d-4e70-9c9f-1b3d5f708cb4","label":"Municipio","question_es":"¿En qué municipio vives?","question_en":"What city do you live in?","answer_type":"text","options":null,"scope":"patient","needed":true,"all_services":true,"service_ids":[],"contact_field_id":"5a7c9e1b-3d5f-4a7b-9c1d-3e5f7a9b1c2d","position":0,"archived":false,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}]}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"orderClinicRecordFields"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `field_ids` names something that is not yours (`details.ids`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/record-fields/{id}":{"get":{"operationId":"getClinicRecordField","summary":"Get a question","description":"Returns one question of the patient record (archived ones too).\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#getClinicRecordField"},"parameters":[{"name":"id","in":"path","required":true,"description":"The question id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The question.","content":{"application/json":{"example":{"success":true,"data":{"id":"c06c8eaf-4b5d-4e70-9c9f-1b3d5f708cb4","label":"Municipio","question_es":"¿En qué municipio vives?","question_en":"What city do you live in?","answer_type":"text","options":null,"scope":"patient","needed":true,"all_services":true,"service_ids":[],"contact_field_id":"5a7c9e1b-3d5f-4a7b-9c1d-3e5f7a9b1c2d","position":0,"archived":false,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No question with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateClinicRecordField","summary":"Update a question","description":"Changes the fields you send. A new `label` renames the question's contact field; its key stays. `archived: false` brings an archived question back, and it goes alone: an archived question takes no other change.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#updateClinicRecordField"},"parameters":[{"name":"id","in":"path","required":true,"description":"The question id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"description":"The name on the appointment. A question about the patient names its contact field with it.","type":"string","minLength":1,"maxLength":80},"question_es":{"description":"The question in Spanish. The AI asks in its own words.","type":["string","null"],"maxLength":300},"question_en":{"description":"The question in English.","type":["string","null"],"maxLength":300},"answer_type":{"description":"What the answer is. It stays once the question holds answers.","type":"string","enum":["text","long_text","date","choice","yes_no","number"]},"options":{"description":"The choices of a `choice` question. A `yes_no` question has Sí and No unless you name two others.","type":["array","null"],"minItems":1,"maxItems":30,"items":{"description":"One choice.","type":"string","minLength":1,"maxLength":80}},"scope":{"description":"`patient` asks once and saves on a contact field, which the save creates or reuses. `visit` asks every appointment and saves on it. It stays once the question holds answers.","type":"string","enum":["patient","visit"]},"needed":{"description":"Needed before the visit: a gap shows as missing and is asked once more. It never blocks a booking (default false).","type":"boolean"},"all_services":{"description":"Asked for every service (default true).","type":"boolean"},"service_ids":{"description":"Asked only for these services: naming them turns `all_services` off. An empty list never means every service.","type":"array","maxItems":500,"items":{"description":"A service's id.","type":"string","format":"uuid"}},"archived":{"description":"`false` brings an archived question back. Archiving is the DELETE.","type":"boolean"}}},"example":{"needed":false}}}},"responses":{"200":{"description":"The question, as saved.","content":{"application/json":{"example":{"success":true,"data":{"id":"c06c8eaf-4b5d-4e70-9c9f-1b3d5f708cb4","label":"Municipio","question_es":"¿En qué municipio vives?","question_en":"What city do you live in?","answer_type":"text","options":null,"scope":"patient","needed":true,"all_services":true,"service_ids":[],"contact_field_id":"5a7c9e1b-3d5f-4a7b-9c1d-3e5f7a9b1c2d","position":0,"archived":false,"created_at":"2026-10-12T16:02:11.402+00:00","updated_at":"2026-10-12T16:02:11.402+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicRecordField"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A rule of the question: `details.reason` says which (`RECORD_FIELD_INVALID`, `RECORD_FIELD_LABEL_UNUSABLE`, `RECORD_FIELD_UNKNOWN_SERVICE`). `service_ids` names something that is not yours (`details.ids`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No question with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `details.reason`: `RECORD_FIELD_LIMIT` (12 active questions), `RECORD_FIELD_IN_USE` (its answer type or scope can't change once it holds answers), `RECORD_FIELD_ARCHIVED` (bring it back first), or the contact field its name lands on can't take it: `RECORD_FIELD_NAME_ARCHIVED`, `RECORD_FIELD_NAME_CONFLICT`, `RECORD_FIELD_NAME_TAKEN`, `RECORD_FIELD_CONTACT_FIELDS_LIMIT`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"archiveClinicRecordField","summary":"Archive a question","description":"Archives the question: nothing asks it any more, and what patients answered stays. Archiving an archived question changes nothing.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#archiveClinicRecordField"},"parameters":[{"name":"id","in":"path","required":true,"description":"The question id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The question is archived.","content":{"application/json":{"example":{"success":true,"data":{"id":"c06c8eaf-4b5d-4e70-9c9f-1b3d5f708cb4","archived":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"archiveClinicRecordField"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No question with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/settings":{"get":{"operationId":"getClinicSettings","summary":"Get clinic settings","description":"Returns the clinic-wide settings: booking several services at once, how the AI agents offer and start appointments, the attendance recorded when nobody records it, and the round robin of new patients among doctors with each service's roster.\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#getClinicSettings"},"parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The settings.","content":{"application/json":{"example":{"success":true,"data":{"allow_manual_simultaneous_services":false,"ai_start_grid_minutes":30,"ai_offer_policy":{"enabled":true,"days":2,"options_per_daypart":2,"afternoon_start_minute":780,"rules":[{"day_of_week":6,"start_minute":600,"end_minute":720,"kind":"blocked"}]},"attendance_after_hours":24,"attendance_staff_bookings":"completed","attendance_ai_bookings":"completed_if_confirmed","round_robin":{"enabled":true,"preference_consumes_turn":false,"annul_on_switch":true,"keep_existing_patients":true,"full_agenda_behavior":"advance","full_agenda_days":7,"services":[{"service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","enabled":true,"doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"]}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateClinicSettings","summary":"Update clinic settings","description":"Changes the settings you send and returns them all. Service rosters are saved first, then the round-robin switches, then the clinic-wide settings, as the dashboard saves them; when one is refused, the ones before it stay saved.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler (the same rules as the dashboard), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#updateClinicSettings"},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"allow_manual_simultaneous_services":{"description":"Reception (and the API) may book several services for one patient at the same time.","type":"boolean"},"ai_start_grid_minutes":{"description":"The minutes the AI agents may start an appointment on: 15 (any start the hours give), 30 (:00 and :30) or 60 (on the hour). Never applies to reception's bookings.","type":"integer"},"ai_offer_policy":{"description":"How the AI agents offer times (the whole policy; every field required). Never applies to reception's bookings.","type":"object","properties":{"enabled":{"description":"false switches the rules off.","type":"boolean"},"days":{"description":"Lead with the first this many dates that have openings.","type":"integer","minimum":1,"maximum":5},"options_per_daypart":{"description":"Times offered per morning and per afternoon.","type":"integer","minimum":1,"maximum":3},"afternoon_start_minute":{"description":"Minutes after midnight the afternoon starts (on the hour or half hour, 10:00 to 18:00).","type":"integer","minimum":600,"maximum":1080},"rules":{"description":"Weekly times the agents offer first (`preferred`) or never (`blocked`).","type":"array","maxItems":672,"items":{"description":"A rule.","type":"object","properties":{"day_of_week":{"description":"0 = Sunday … 6 = Saturday.","type":"integer","minimum":0,"maximum":6},"start_minute":{"description":"Minutes after midnight, a multiple of 15.","type":"integer","minimum":0,"maximum":1425},"end_minute":{"description":"After start_minute, a multiple of 15.","type":"integer","minimum":15,"maximum":1440},"kind":{"description":"Offered first, or never offered.","type":"string","enum":["preferred","blocked"]}},"required":["day_of_week","start_minute","end_minute","kind"]}}},"required":["enabled","days","options_per_daypart","afternoon_start_minute","rules"]},"attendance_after_hours":{"description":"Attendance when nobody records it: hours after a visit's end (2, 6, 12, 24 or 48; default 24) before NuTalk closes a visit nobody marked Attended or No-show, as `attendance_staff_bookings` or `attendance_ai_bookings` says. Only a key created by an owner or admin can change it.","type":"integer","minimum":2,"maximum":48},"attendance_staff_bookings":{"description":"What a visit your team booked (dashboard, API, import) becomes then: `completed` (attended; the default), `completed_if_confirmed` (attended if the patient confirmed it, `unconfirmed` otherwise) or `unconfirmed`. Never a no-show. Owners' and admins' keys only.","type":"string","enum":["completed","completed_if_confirmed","unconfirmed"]},"attendance_ai_bookings":{"description":"The same for a visit an AI agent booked: `completed`, `completed_if_confirmed` or `unconfirmed` (the default). Owners' and admins' keys only.","type":"string","enum":["completed","completed_if_confirmed","unconfirmed"]},"round_robin":{"description":"The round robin of new patients among doctors. Only the fields sent change.","type":"object","properties":{"enabled":{"description":"The master switch.","type":"boolean"},"preference_consumes_turn":{"description":"A patient who asks for a doctor still counts as that doctor's turn.","type":"boolean"},"annul_on_switch":{"description":"When the conversation books another doctor, the first assignment is annulled.","type":"boolean"},"keep_existing_patients":{"description":"Returning patients keep their doctor.","type":"boolean"},"full_agenda_behavior":{"description":"When the assigned doctor has no opening soon: `advance` passes the patient to the next doctor, `wait` keeps offering the assigned doctor.","type":"string","enum":["advance","wait"]},"full_agenda_days":{"description":"What \"soon\" means, in days.","type":"integer","minimum":1,"maximum":30},"services":{"description":"Per-service rosters to save (each replaces that service's roster; services not listed keep theirs). Saved before the switches.","type":"array","maxItems":200,"items":{"description":"A service's roster.","type":"object","properties":{"service_id":{"description":"The service.","type":"string","format":"uuid"},"enabled":{"description":"Distribute this service's new patients.","type":"boolean"},"doctor_ids":{"description":"The doctors, in turn order (at least one when enabled; newly added ones must be active).","type":"array","maxItems":50,"items":{"description":"A doctor.","type":"string","format":"uuid"}}},"required":["service_id","enabled","doctor_ids"]}}}}}},"example":{"ai_start_grid_minutes":30,"round_robin":{"enabled":true,"services":[{"service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","enabled":true,"doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"]}]}}}}},"responses":{"200":{"description":"Every setting, as saved.","content":{"application/json":{"example":{"success":true,"data":{"allow_manual_simultaneous_services":false,"ai_start_grid_minutes":30,"ai_offer_policy":{"enabled":true,"days":2,"options_per_daypart":2,"afternoon_start_minute":780,"rules":[{"day_of_week":6,"start_minute":600,"end_minute":720,"kind":"blocked"}]},"attendance_after_hours":24,"attendance_staff_bookings":"completed","attendance_ai_bookings":"completed_if_confirmed","round_robin":{"enabled":true,"preference_consumes_turn":false,"annul_on_switch":true,"keep_existing_patients":true,"full_agenda_behavior":"advance","full_agenda_days":7,"services":[{"service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","enabled":true,"doctor_ids":["3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"]}]}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicSettings"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `ai_offer_policy` is not a whole valid policy, a roster names a service or doctor that is not yours, or newly adds an inactive doctor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`internal_error`: A setting needs a release that is not deployed yet: retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clinic/reminders":{"get":{"operationId":"getClinicReminders","summary":"Get the clinic's reminders","description":"Returns the clinic's appointment reminders, as Clinic Settings › Reminders shows them: whether they go out, the WhatsApp channel they go out on (`send_from_channel_id`) and the agent that writes them, when, the confirmation, the reminder call, quiet hours, and what happens while your team handles the chat. `channels` lists the channels they can go out on, and `other_agents` any other agent whose own reminders still go out on its channel (changed on that agent, with `PUT /agents/{id}/automations`). `agent_id` and `updated_at` are null until the clinic first saves them; until then each agent keeps its own. `agent_left` is true when that agent answers no channel here any more: none go out (`send_from_channel_id` is null) until a change names `send_from_channel_id`, which moves them to that channel's agent.\n\nRequires the `clinic_setup:read` scope.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#getClinicReminders"},"parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The reminders.","content":{"application/json":{"example":{"success":true,"data":{"enabled":true,"send_from_channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_left":false,"offsets_minutes":[1440,30],"whatsapp_enabled":true,"confirmation_enabled":true,"day_reminders_at_hour":true,"day_reminder_hour":9,"late_booking_reminders":true,"remind_held_chats":false,"voice_enabled":false,"voice_channel_ids":[],"voice_offset_minutes":240,"quiet_hours_enabled":true,"quiet_start_hour":21,"quiet_end_hour":9,"updated_at":"2026-10-12T16:02:11.402+00:00","channels":[{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"WA Recepción","agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Lía","ai_mode":"live"}],"other_agents":[]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"updateClinicReminders","summary":"Change the clinic's reminders","description":"Changes the settings you send, over what is saved at that moment, and returns them all. From then on reminders and reminder calls go out to patients on their own. A deploy or a rollback of the agent doesn't change them. `PUT /agents/{id}/automations` still changes them through the agent that sends them.\n\nRequires the `clinic_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Settings handler, acting as the member who created the key: an owner, admin, builder or clinic manager. It is recorded as the key's.","tags":["Clinic setup"],"x-required-scope":"clinic_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/clinic#updateClinicReminders"},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"Remind patients of their appointments.","type":"boolean"},"send_from_channel_id":{"description":"The WhatsApp channel they go out on, one of `channels`: the agent live on it writes them. Once the clinic has saved, only that agent's channels, unless none of them is left.","type":"string","format":"uuid"},"offsets_minutes":{"description":"Minutes before the appointment for each WhatsApp reminder (at most 3).","type":"array","minItems":1,"maxItems":3,"items":{"description":"Minutes.","type":"integer","minimum":15,"maximum":10080}},"whatsapp_enabled":{"description":"Send WhatsApp reminders.","type":"boolean"},"confirmation_enabled":{"description":"Ask the patient to confirm, and record when they do.","type":"boolean"},"day_reminders_at_hour":{"description":"Send a reminder set in whole days (1 day, 2 days…) at `day_reminder_hour` of that day instead of at the appointment's hour.","type":"boolean"},"day_reminder_hour":{"description":"Local hour day reminders go out at.","type":"integer","minimum":0,"maximum":23},"late_booking_reminders":{"description":"An appointment booked after its day reminder's moment, for a later day, gets that reminder right away.","type":"boolean"},"remind_held_chats":{"description":"While your team handles the chat, reminders keep going: the team, not the AI, reads the reply. Off by default: reminders stop. A patient's own exception, set in the dashboard, wins. In a workspace that also runs Meetings it is set with `PUT /agents/{id}/automations`, for both.","type":"boolean"},"voice_enabled":{"description":"Also place a reminder call, from the voice channels the same agent answers (at least one).","type":"boolean"},"voice_offset_minutes":{"description":"Minutes before the appointment for the reminder call.","type":"integer","minimum":15,"maximum":10080},"quiet_hours_enabled":{"description":"Hold reminders during quiet hours; due ones go out after.","type":"boolean"},"quiet_start_hour":{"description":"Local hour quiet hours start.","type":"integer","minimum":0,"maximum":23},"quiet_end_hour":{"description":"Local hour quiet hours end.","type":"integer","minimum":0,"maximum":23}}},"example":{"enabled":true,"offsets_minutes":[1440,120],"late_booking_reminders":true}}}},"responses":{"200":{"description":"The reminders, as saved.","content":{"application/json":{"example":{"success":true,"data":{"enabled":true,"send_from_channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_left":false,"offsets_minutes":[1440,30],"whatsapp_enabled":true,"confirmation_enabled":true,"day_reminders_at_hour":true,"day_reminder_hour":9,"late_booking_reminders":true,"remind_held_chats":false,"voice_enabled":false,"voice_channel_ids":[],"voice_offset_minutes":240,"quiet_hours_enabled":true,"quiet_start_hour":21,"quiet_end_hour":9,"updated_at":"2026-10-12T16:02:11.402+00:00","channels":[{"id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","name":"WA Recepción","agent_id":"550e8400-e29b-41d4-a716-446655440000","agent_name":"Lía","ai_mode":"live"}],"other_agents":[]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicReminders"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `send_from_channel_id` is not one of `channels`, no channel can send them yet, the reminder call has no voice channel the same agent answers, or `remind_held_chats` changes in a workspace that also runs Meetings (until Meetings get their own reminders, the agent's automations set it, for both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The reminders changed while saving: try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/hosts":{"get":{"operationId":"listBusinessHosts","summary":"List hosts","description":"Lists the people who take meetings, by name.\n\nRequires the `business_setup:read` scope.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#listBusinessHosts"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Active hosts (the default) or archived ones.","schema":{"type":"string","enum":["active","archived"],"default":"active"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of hosts.","content":{"application/json":{"example":{"success":true,"data":[{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","name":"Ana López","email":"ana@example.com","title":"Account executive","timezone":"America/Monterrey","status":"active","user_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","hubspot_owner_ids":["123456789"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createBusinessHost","summary":"Create a host","description":"Adds a person who takes meetings. Give them weekly hours (`POST /v1/business/hosts/:id/availability`) before they can be booked.\n\nRequires the `business_setup:write` scope.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#createBusinessHost"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The host's name.","type":"string","minLength":1,"maxLength":200},"email":{"description":"The host's email.","type":["string","null"],"format":"email","maxLength":320},"title":{"description":"Their role, shown to the team.","type":["string","null"],"maxLength":200},"timezone":{"description":"The host's time zone (an IANA name such as `America/Monterrey`): their weekly hours are read on this clock. Null or omitted: the workspace's time zone.","type":["string","null"],"maxLength":100}},"required":["name"]},"example":{"name":"Ana López","email":"ana@example.com","title":"Account executive","timezone":"America/Monterrey"}}}},"responses":{"201":{"description":"The host.","content":{"application/json":{"example":{"success":true,"data":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","name":"Ana López","email":"ana@example.com","title":"Account executive","timezone":"America/Monterrey","status":"active","user_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","hubspot_owner_ids":["123456789"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `timezone` is not a time zone name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/hosts/{id}":{"get":{"operationId":"getBusinessHost","summary":"Get a host","description":"Returns one host, active or archived.\n\nRequires the `business_setup:read` scope.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#getBusinessHost"},"parameters":[{"name":"id","in":"path","required":true,"description":"The host id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The host.","content":{"application/json":{"example":{"success":true,"data":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","name":"Ana López","email":"ana@example.com","title":"Account executive","timezone":"America/Monterrey","status":"active","user_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","hubspot_owner_ids":["123456789"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No host with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateBusinessHost","summary":"Update a host","description":"Changes the fields you send; null clears `email`, `title` or `timezone`, unlinks the login (`user_id`) or clears the HubSpot owners.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\n`user_id` is the NuTalk login the host is: that person sees the host's meetings under My meetings and gets the host's own calendar link (null: no login). `hubspot_owner_ids` are the HubSpot owners the host stands for: when the agents book a HubSpot lead, a lead owned by one of them is booked with this host.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#updateBusinessHost"},"parameters":[{"name":"id","in":"path","required":true,"description":"The host id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"New name.","type":"string","minLength":1,"maxLength":200},"email":{"description":"New email.","type":["string","null"],"format":"email","maxLength":320},"title":{"description":"New role.","type":["string","null"],"maxLength":200},"timezone":{"description":"The host's time zone (an IANA name such as `America/Monterrey`): their weekly hours are read on this clock. Null or omitted: the workspace's time zone.","type":["string","null"],"maxLength":100},"user_id":{"description":"Link the host to this member's NuTalk login (a member of this workspace no other active host is linked to: one person is one host). Null unlinks.","type":["string","null"],"format":"uuid"},"hubspot_owner_ids":{"description":"The HubSpot owners this host stands for: each is HubSpot's numeric owner id. Replaces the list; null or `[]` clears it.","type":["array","null"],"maxItems":10,"items":{"description":"A HubSpot owner id (digits only).","type":"string","maxLength":32}}}},"example":{"title":"Sales lead","user_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","hubspot_owner_ids":["123456789"]}}}},"responses":{"200":{"description":"The updated host.","content":{"application/json":{"example":{"success":true,"data":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","name":"Ana López","email":"ana@example.com","title":"Sales lead","timezone":"America/Monterrey","status":"active","user_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","hubspot_owner_ids":["123456789"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateBusinessHost"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent, `timezone` is not a time zone name, `user_id` is not a member of this workspace, or a HubSpot owner id is not digits only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…). `forbidden`: `user_id` was sent and the member who created the key is not an owner, admin or builder: only they link a host to a login.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No host with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: `user_id` is already linked to another active host. Unlink it there first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"archiveBusinessHost","summary":"Archive a host","description":"Archives the host: they are no longer offered or booked. Their booked meetings stay booked; agents whose business scheduler defaults to this host offer no times until another default is chosen.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#archiveBusinessHost"},"parameters":[{"name":"id","in":"path","required":true,"description":"The host id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The archived host.","content":{"application/json":{"example":{"success":true,"data":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","name":"Ana López","email":"ana@example.com","title":"Account executive","timezone":"America/Monterrey","status":"archived","user_id":"4c8e2a6b-1d3f-4e5a-9b7c-0f2d4a6c8e14","hubspot_owner_ids":["123456789"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"archiveBusinessHost"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No host with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/hosts/{id}/availability":{"get":{"operationId":"listBusinessHostAvailability","summary":"List a host's weekly hours","description":"Lists every weekly window of the host, switched on or off, by weekday and start time. Meetings are offered inside active windows only, on the host's clock.\n\nRequires the `business_setup:read` scope.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#listBusinessHostAvailability"},"parameters":[{"name":"id","in":"path","required":true,"description":"The host id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The host's windows.","content":{"application/json":{"example":{"success":true,"data":[{"id":"3a4b5c6d-7e8f-4a9b-8c0d-1e2f3a4b5c6d","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","day_of_week":1,"start_time":"09:00","end_time":"13:00","is_active":true,"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"},{"id":"4b5c6d7e-8f9a-4b0c-9d1e-2f3a4b5c6d7e","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","day_of_week":1,"start_time":"14:00","end_time":"18:00","is_active":true,"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No host with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"addBusinessHostAvailability","summary":"Add weekly hours","description":"Adds the same hours on one or more weekdays (one window per day). All or nothing: when the hours overlap the host's existing hours on any of the days, nothing is added.\n\nRequires the `business_setup:write` scope.\n\nA host's active windows on one weekday never overlap: overlapping hours read as one continuous run and would erase the break between them (a lunch break is two windows, 09:00–13:00 and 14:00–18:00). Windows that only touch (13:00 end, 13:00 start) are fine.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#addBusinessHostAvailability"},"parameters":[{"name":"id","in":"path","required":true,"description":"The host id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"days_of_week":{"description":"The weekdays. Weekday: 0 = Sunday, 1 = Monday … 6 = Saturday.","type":"array","minItems":1,"maxItems":7,"items":{"description":"Weekday: 0 = Sunday, 1 = Monday … 6 = Saturday.","type":"integer","minimum":0,"maximum":6}},"start_time":{"description":"HH:MM, 24-hour (00:00–23:59), on the host's clock.","type":"string","minLength":5,"maxLength":5},"end_time":{"description":"HH:MM, 24-hour (00:00–23:59), on the host's clock. After start_time; a meeting must end inside the window.","type":"string","minLength":5,"maxLength":5}},"required":["days_of_week","start_time","end_time"]},"example":{"days_of_week":[1,2,3,4,5],"start_time":"09:00","end_time":"13:00"}}}},"responses":{"201":{"description":"The windows added, one per weekday.","content":{"application/json":{"example":{"success":true,"data":[{"id":"3a4b5c6d-7e8f-4a9b-8c0d-1e2f3a4b5c6d","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","day_of_week":1,"start_time":"09:00","end_time":"13:00","is_active":true,"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}]}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `end_time` is not after `start_time`, or the hours overlap the host's existing hours (the message names the window).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No host with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/availability-windows/{id}":{"patch":{"operationId":"updateBusinessAvailabilityWindow","summary":"Update a weekly window","description":"Changes one window's weekday or hours, or switches it off (`is_active: false`) and on again. The window as it will read must not overlap the host's other active windows that day.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nA host's active windows on one weekday never overlap: overlapping hours read as one continuous run and would erase the break between them (a lunch break is two windows, 09:00–13:00 and 14:00–18:00). Windows that only touch (13:00 end, 13:00 start) are fine.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#updateBusinessAvailabilityWindow"},"parameters":[{"name":"id","in":"path","required":true,"description":"The availability window id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"day_of_week":{"description":"Weekday: 0 = Sunday, 1 = Monday … 6 = Saturday.","type":"integer","minimum":0,"maximum":6},"start_time":{"description":"HH:MM, 24-hour (00:00–23:59), on the host's clock.","type":"string","minLength":5,"maxLength":5},"end_time":{"description":"HH:MM, 24-hour (00:00–23:59), on the host's clock.","type":"string","minLength":5,"maxLength":5},"is_active":{"description":"Switch the window on or off (an inactive window offers nothing).","type":"boolean"}}},"example":{"end_time":"12:30"}}}},"responses":{"200":{"description":"The updated window.","content":{"application/json":{"example":{"success":true,"data":{"id":"3a4b5c6d-7e8f-4a9b-8c0d-1e2f3a4b5c6d","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","day_of_week":1,"start_time":"09:00","end_time":"12:30","is_active":true,"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateBusinessAvailabilityWindow"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent, `end_time` is not after `start_time`, or the window would overlap the host's other hours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No availability window with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteBusinessAvailabilityWindow","summary":"Delete a weekly window","description":"Removes one window for good; booked meetings stay. To pause it instead, set `is_active: false`.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#deleteBusinessAvailabilityWindow"},"parameters":[{"name":"id","in":"path","required":true,"description":"The availability window id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The window was deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"3a4b5c6d-7e8f-4a9b-8c0d-1e2f3a4b5c6d","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteBusinessAvailabilityWindow"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No availability window with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/hosts/{id}/days-off":{"get":{"operationId":"listBusinessHostDaysOff","summary":"List a host's days off","description":"Lists the host's time off, oldest first. Company-wide closures are listed by `GET /v1/business/closures`.\n\nRequires the `business_setup:read` scope.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#listBusinessHostDaysOff"},"parameters":[{"name":"id","in":"path","required":true,"description":"The host id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"from","in":"query","required":false,"description":"Only the ones that end after this time (ISO 8601 with an offset or Z).","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"Only the ones that start before this time.","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of days off.","content":{"application/json":{"example":{"success":true,"data":[{"id":"5c6d7e8f-9a0b-4c1d-ae2f-3a4b5c6d7e8f","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","starts_at":"2026-12-24T06:00:00+00:00","ends_at":"2026-12-26T06:00:00+00:00","reason":"Vacation","created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No host with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"addBusinessHostDayOff","summary":"Add a day off","description":"Blocks the host from `starts_at` to `ends_at`: no meeting is offered or booked with them in that time. Meetings already booked then stay booked.\n\nRequires the `business_setup:write` scope.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#addBusinessHostDayOff"},"parameters":[{"name":"id","in":"path","required":true,"description":"The host id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"starts_at":{"description":"When it starts (ISO 8601 with an offset or Z). A whole day starts at its local midnight.","type":"string","format":"date-time"},"ends_at":{"description":"When it ends, after starts_at (the next local midnight for a whole day).","type":"string","format":"date-time"},"reason":{"description":"A note for your team.","type":["string","null"],"maxLength":500}},"required":["starts_at","ends_at"]},"example":{"starts_at":"2026-12-24T00:00:00-06:00","ends_at":"2026-12-26T00:00:00-06:00","reason":"Vacation"}}}},"responses":{"201":{"description":"The day off.","content":{"application/json":{"example":{"success":true,"data":{"id":"5c6d7e8f-9a0b-4c1d-ae2f-3a4b5c6d7e8f","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","starts_at":"2026-12-24T06:00:00+00:00","ends_at":"2026-12-26T06:00:00+00:00","reason":"Vacation","created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `ends_at` is not after `starts_at` (as it will read).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No host with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/days-off/{id}":{"patch":{"operationId":"updateBusinessDayOff","summary":"Update a day off","description":"Changes a host's day off; null clears the reason.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#updateBusinessDayOff"},"parameters":[{"name":"id","in":"path","required":true,"description":"The day off id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"starts_at":{"description":"When it starts (ISO 8601 with an offset or Z). A whole day starts at its local midnight.","type":"string","format":"date-time"},"ends_at":{"description":"When it ends, after starts_at (the next local midnight for a whole day).","type":"string","format":"date-time"},"reason":{"description":"A note for your team.","type":["string","null"],"maxLength":500}}},"example":{"ends_at":"2026-12-27T00:00:00-06:00"}}}},"responses":{"200":{"description":"The updated day off.","content":{"application/json":{"example":{"success":true,"data":{"id":"5c6d7e8f-9a0b-4c1d-ae2f-3a4b5c6d7e8f","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","starts_at":"2026-12-24T06:00:00+00:00","ends_at":"2026-12-27T06:00:00+00:00","reason":"Vacation","created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateBusinessDayOff"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent. `validation_error`: `ends_at` is not after `starts_at` (as it will read).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No day off with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteBusinessDayOff","summary":"Delete a day off","description":"Removes a host's day off: the time is bookable again within their weekly hours.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#deleteBusinessDayOff"},"parameters":[{"name":"id","in":"path","required":true,"description":"The day off id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The day off was deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"5c6d7e8f-9a0b-4c1d-ae2f-3a4b5c6d7e8f","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteBusinessDayOff"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No day off with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/closures":{"get":{"operationId":"listBusinessClosures","summary":"List closures","description":"Lists company-wide closures (every host is blocked), oldest first.\n\nRequires the `business_setup:read` scope.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#listBusinessClosures"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"from","in":"query","required":false,"description":"Only the ones that end after this time (ISO 8601 with an offset or Z).","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"Only the ones that start before this time.","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of closures.","content":{"application/json":{"example":{"success":true,"data":[{"id":"6d7e8f9a-0b1c-4d2e-bf3a-4b5c6d7e8f9a","starts_at":"2027-01-01T06:00:00+00:00","ends_at":"2027-01-02T06:00:00+00:00","reason":"New Year's Day","created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"addBusinessClosure","summary":"Add a closure","description":"Closes the business from `starts_at` to `ends_at` (a holiday, an event): no host is offered or booked in that time.\n\nRequires the `business_setup:write` scope.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#addBusinessClosure"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"starts_at":{"description":"When it starts (ISO 8601 with an offset or Z). A whole day starts at its local midnight.","type":"string","format":"date-time"},"ends_at":{"description":"When it ends, after starts_at (the next local midnight for a whole day).","type":"string","format":"date-time"},"reason":{"description":"A note for your team.","type":["string","null"],"maxLength":500}},"required":["starts_at","ends_at"]},"example":{"starts_at":"2027-01-01T00:00:00-06:00","ends_at":"2027-01-02T00:00:00-06:00","reason":"New Year's Day"}}}},"responses":{"201":{"description":"The closure.","content":{"application/json":{"example":{"success":true,"data":{"id":"6d7e8f9a-0b1c-4d2e-bf3a-4b5c6d7e8f9a","starts_at":"2027-01-01T06:00:00+00:00","ends_at":"2027-01-02T06:00:00+00:00","reason":"New Year's Day","created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: `ends_at` is not after `starts_at` (as it will read).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/closures/{id}":{"patch":{"operationId":"updateBusinessClosure","summary":"Update a closure","description":"Changes a company-wide closure; null clears the reason.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#updateBusinessClosure"},"parameters":[{"name":"id","in":"path","required":true,"description":"The closure id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"starts_at":{"description":"When it starts (ISO 8601 with an offset or Z). A whole day starts at its local midnight.","type":"string","format":"date-time"},"ends_at":{"description":"When it ends, after starts_at (the next local midnight for a whole day).","type":"string","format":"date-time"},"reason":{"description":"A note for your team.","type":["string","null"],"maxLength":500}}},"example":{"reason":"Holiday"}}}},"responses":{"200":{"description":"The updated closure.","content":{"application/json":{"example":{"success":true,"data":{"id":"6d7e8f9a-0b1c-4d2e-bf3a-4b5c6d7e8f9a","starts_at":"2027-01-01T06:00:00+00:00","ends_at":"2027-01-02T06:00:00+00:00","reason":"Holiday","created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateBusinessClosure"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent. `validation_error`: `ends_at` is not after `starts_at` (as it will read).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No closure with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteBusinessClosure","summary":"Delete a closure","description":"Removes a company-wide closure.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#deleteBusinessClosure"},"parameters":[{"name":"id","in":"path","required":true,"description":"The closure id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The closure was deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"6d7e8f9a-0b1c-4d2e-bf3a-4b5c6d7e8f9a","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteBusinessClosure"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No closure with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/meeting-types":{"get":{"operationId":"listBusinessMeetingTypes","summary":"List meeting types","description":"Lists meeting types in the dashboard's order, each with the hosts who take it (`host_ids`; empty means every active host).\n\nRequires the `business_setup:read` scope.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#listBusinessMeetingTypes"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Active meeting types (the default) or archived ones.","schema":{"type":"string","enum":["active","archived"],"default":"active"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of meeting types.","content":{"application/json":{"example":{"success":true,"data":[{"id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","name":"Intro call","slug":"intro-call","description":"A 30-minute first conversation.","duration_minutes":30,"buffer_before_minutes":0,"buffer_after_minutes":10,"slot_interval_minutes":30,"location_kind":"google_meet","location_details":null,"min_notice_minutes":240,"max_advance_days":60,"color":"#3B82F6","sort_order":0,"status":"active","host_ids":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","7e8f9a0b-1c2d-4e3f-9a4b-5c6d7e8f9a0b"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createBusinessMeetingType","summary":"Create a meeting type","description":"Creates a kind of meeting customers and agents can book: its length, buffers, start-time steps, location and booking window, and the hosts who take it.\n\nRequires the `business_setup:write` scope.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#createBusinessMeetingType"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The name customers and agents see, e.g. Intro call.","type":"string","minLength":1,"maxLength":200},"slug":{"description":"Lowercase letters, digits and dashes, unique among the workspace's active meeting types. Omitted: made from the name.","type":"string","maxLength":60},"description":{"description":"What the meeting is for.","type":["string","null"],"maxLength":2000},"duration_minutes":{"description":"How long the meeting lasts.","type":"integer","default":30,"minimum":5,"maximum":480},"buffer_before_minutes":{"description":"Free time the host needs before it.","type":"integer","default":0,"minimum":0,"maximum":120},"buffer_after_minutes":{"description":"Free time the host needs after it.","type":"integer","default":0,"minimum":0,"maximum":120},"slot_interval_minutes":{"description":"Offered start times step by this many minutes from the start of each window.","type":"integer","default":30,"minimum":5,"maximum":120},"location_kind":{"description":"`google_meet` adds a Meet link when the host's Google calendar is connected; `phone`, `in_person` and `custom` use `location_details`.","type":"string","enum":["google_meet","phone","in_person","custom"],"default":"google_meet"},"location_details":{"description":"The address, phone number or instructions.","type":["string","null"],"maxLength":1000},"min_notice_minutes":{"description":"How far ahead a meeting must be booked.","type":"integer","default":240,"minimum":0,"maximum":10080},"max_advance_days":{"description":"How many days ahead it can be booked.","type":"integer","default":60,"minimum":1,"maximum":365},"color":{"description":"A hex color for the dashboard, e.g. #3B82F6.","type":["string","null"],"maxLength":7},"sort_order":{"description":"Position in lists (lowest first).","type":"integer","default":0,"minimum":0,"maximum":999},"host_ids":{"description":"The hosts who take this meeting type, in priority order. Omitted or empty: every active host.","type":"array","maxItems":200,"items":{"description":"A host id.","type":"string","format":"uuid"}}},"required":["name"]},"example":{"name":"Intro call","description":"A 30-minute first conversation.","duration_minutes":30,"buffer_after_minutes":10,"location_kind":"google_meet","host_ids":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","7e8f9a0b-1c2d-4e3f-9a4b-5c6d7e8f9a0b"]}}}},"responses":{"201":{"description":"The meeting type.","content":{"application/json":{"example":{"success":true,"data":{"id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","name":"Intro call","slug":"intro-call","description":"A 30-minute first conversation.","duration_minutes":30,"buffer_before_minutes":0,"buffer_after_minutes":10,"slot_interval_minutes":30,"location_kind":"google_meet","location_details":null,"min_notice_minutes":240,"max_advance_days":60,"color":"#3B82F6","sort_order":0,"status":"active","host_ids":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","7e8f9a0b-1c2d-4e3f-9a4b-5c6d7e8f9a0b"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A host in `host_ids` is not a host of your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: An active meeting type already uses the slug (send another name or a `slug`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/meeting-types/{id}":{"get":{"operationId":"getBusinessMeetingType","summary":"Get a meeting type","description":"Returns one meeting type, active or archived, with its hosts.\n\nRequires the `business_setup:read` scope.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#getBusinessMeetingType"},"parameters":[{"name":"id","in":"path","required":true,"description":"The meeting type id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The meeting type.","content":{"application/json":{"example":{"success":true,"data":{"id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","name":"Intro call","slug":"intro-call","description":"A 30-minute first conversation.","duration_minutes":30,"buffer_before_minutes":0,"buffer_after_minutes":10,"slot_interval_minutes":30,"location_kind":"google_meet","location_details":null,"min_notice_minutes":240,"max_advance_days":60,"color":"#3B82F6","sort_order":0,"status":"active","host_ids":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","7e8f9a0b-1c2d-4e3f-9a4b-5c6d7e8f9a0b"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No meeting type with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateBusinessMeetingType","summary":"Update a meeting type","description":"Changes the fields you send. `host_ids` replaces the hosts who take it (an empty list means every active host); the new hosts are added before the old ones are removed, so a failure part-way never opens the type to every host.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#updateBusinessMeetingType"},"parameters":[{"name":"id","in":"path","required":true,"description":"The meeting type id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The name customers and agents see, e.g. Intro call.","type":"string","minLength":1,"maxLength":200},"description":{"description":"What the meeting is for.","type":["string","null"],"maxLength":2000},"duration_minutes":{"description":"How long the meeting lasts (booked meetings keep their length).","type":"integer","minimum":5,"maximum":480},"buffer_before_minutes":{"description":"Free time the host needs before it.","type":"integer","minimum":0,"maximum":120},"buffer_after_minutes":{"description":"Free time the host needs after it.","type":"integer","minimum":0,"maximum":120},"slot_interval_minutes":{"description":"Offered start times step by this many minutes from the start of each window.","type":"integer","minimum":5,"maximum":120},"location_kind":{"description":"`google_meet` adds a Meet link when the host's Google calendar is connected; `phone`, `in_person` and `custom` use `location_details`.","type":"string","enum":["google_meet","phone","in_person","custom"]},"location_details":{"description":"The address, phone number or instructions.","type":["string","null"],"maxLength":1000},"min_notice_minutes":{"description":"How far ahead a meeting must be booked.","type":"integer","minimum":0,"maximum":10080},"max_advance_days":{"description":"How many days ahead it can be booked.","type":"integer","minimum":1,"maximum":365},"color":{"description":"A hex color for the dashboard, e.g. #3B82F6.","type":["string","null"],"maxLength":7},"sort_order":{"description":"Position in lists (lowest first).","type":"integer","minimum":0,"maximum":999},"host_ids":{"description":"Replaces the hosts who take it, in priority order; an empty list means every active host.","type":"array","maxItems":200,"items":{"description":"A host id.","type":"string","format":"uuid"}}}},"example":{"duration_minutes":45,"host_ids":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]}}}},"responses":{"200":{"description":"The updated meeting type.","content":{"application/json":{"example":{"success":true,"data":{"id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","name":"Intro call","slug":"intro-call","description":"A 30-minute first conversation.","duration_minutes":45,"buffer_before_minutes":0,"buffer_after_minutes":10,"slot_interval_minutes":30,"location_kind":"google_meet","location_details":null,"min_notice_minutes":240,"max_advance_days":60,"color":"#3B82F6","sort_order":0,"status":"active","host_ids":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateBusinessMeetingType"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent, or a host in `host_ids` is not a host of your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No meeting type with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"archiveBusinessMeetingType","summary":"Archive a meeting type","description":"Archives the meeting type: it is no longer offered or booked, and its booked meetings stay. Agents whose business scheduler defaults to it offer no times until another default is chosen.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#archiveBusinessMeetingType"},"parameters":[{"name":"id","in":"path","required":true,"description":"The meeting type id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The archived meeting type.","content":{"application/json":{"example":{"success":true,"data":{"id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","name":"Intro call","slug":"intro-call","description":"A 30-minute first conversation.","duration_minutes":30,"buffer_before_minutes":0,"buffer_after_minutes":10,"slot_interval_minutes":30,"location_kind":"google_meet","location_details":null,"min_notice_minutes":240,"max_advance_days":60,"color":"#3B82F6","sort_order":0,"status":"archived","host_ids":["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","7e8f9a0b-1c2d-4e3f-9a4b-5c6d7e8f9a0b"],"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"archiveBusinessMeetingType"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No meeting type with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/hosts/{id}/calendar-connections":{"get":{"operationId":"listBusinessHostCalendarConnections","summary":"List a host's calendar connections","description":"Lists the calendar accounts connected for the host: Google and Outlook (their busy time blocks the host, and one of them can receive the meetings) and iCloud/CalDAV accounts and iCal links (busy time only). Tokens, passwords and iCal addresses are never returned.\n\nRequires the `business_setup:read` scope.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#listBusinessHostCalendarConnections"},"parameters":[{"name":"id","in":"path","required":true,"description":"The host id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The host's connections.","content":{"application/json":{"example":{"success":true,"data":[{"id":"9a0b1c2d-3e4f-4a5b-9c6d-7e8f9a0b1c2d","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","provider":"google","account":"ana@example.com","calendar_id":"primary","selected_calendar_ids":["primary"],"sync_enabled":true,"connected_at":"2026-10-12T15:04:05.123456+00:00","revoked_at":null,"last_sync_error":null}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No host with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/calendar-connections/{id}/calendars":{"get":{"operationId":"listBusinessCalendarConnectionCalendars","summary":"List a connected account's calendars","description":"Asks the provider for the account's calendars, with the connection's current choice: the ids to use in `selected_calendar_ids` and `calendar_id`.\n\nRequires the `business_setup:read` scope.\n\nRuns as the member who created the key (it reads with their access); reseller keys can't call it.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#listBusinessCalendarConnectionCalendars"},"parameters":[{"name":"id","in":"path","required":true,"description":"The calendar connection id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The account's calendars.","content":{"application/json":{"example":{"success":true,"data":{"calendars":[{"id":"primary","name":"Ana López","primary":true,"writable":true,"color":"#039BE5"},{"id":"team@example.com","name":"Team","primary":false,"writable":false,"color":"#7986CB"}],"selected_calendar_ids":["primary"],"appointment_calendar_id":"primary","sync_enabled":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No calendar connection with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The account must be connected again (its access was revoked).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/calendar-connections/{id}":{"patch":{"operationId":"updateBusinessCalendarConnection","summary":"Update a calendar connection","description":"Chooses which of the account's calendars block the host's time (`selected_calendar_ids`), which one new meetings are written to (`calendar_id`, Google and Outlook only) and whether they are written at all (`sync_enabled`; turning it on turns it off on the host's other accounts).\n\nRequires the `business_setup:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nRuns as the member who created the key: only owners, admins and builders change calendar connections. Connecting a new account (Google, Outlook, iCloud/CalDAV, an iCal link) is done in the dashboard.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#updateBusinessCalendarConnection"},"parameters":[{"name":"id","in":"path","required":true,"description":"The calendar connection id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"selected_calendar_ids":{"description":"The calendars whose busy time blocks the host.","type":"array","minItems":1,"maxItems":50,"items":{"description":"A calendar id from the account's calendar list.","type":"string"}},"calendar_id":{"description":"Where new meetings are written: one of the selected calendars (Google and Outlook accounts).","type":"string","maxLength":1000},"sync_enabled":{"description":"Write the host's meetings to this account (iCal links and CalDAV accounts never receive them).","type":"boolean"}}},"example":{"selected_calendar_ids":["primary","team@example.com"],"calendar_id":"primary"}}}},"responses":{"200":{"description":"The updated connection. For iCal and CalDAV the new selection is read again at once; `sync_error` says why that read failed (the selection is saved either way).","content":{"application/json":{"example":{"success":true,"data":{"id":"9a0b1c2d-3e4f-4a5b-9c6d-7e8f9a0b1c2d","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","provider":"google","account":"ana@example.com","calendar_id":"primary","selected_calendar_ids":["primary","team@example.com"],"sync_enabled":true,"connected_at":"2026-10-12T15:04:05.123456+00:00","revoked_at":null,"last_sync_error":null}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateBusinessCalendarConnection"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent, `calendar_id` is not a selected calendar, a new `selected_calendar_ids` leaves out the calendar meetings are written to without naming another in `calendar_id`, a calendar isn't the account's (iCal and CalDAV accounts), or an iCal/CalDAV account was asked to receive meetings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No calendar connection with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The account was reconnected meanwhile; read it again and retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"disconnectBusinessCalendarConnection","summary":"Disconnect a calendar","description":"Disconnects the account from the host and removes its tokens: its busy time no longer blocks the host, and meetings are no longer written there.\n\nRequires the `business_setup:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nRuns as the member who created the key: only owners, admins and builders change calendar connections. Connecting a new account (Google, Outlook, iCloud/CalDAV, an iCal link) is done in the dashboard.","tags":["Business scheduling setup"],"x-required-scope":"business_setup:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/business-scheduling#disconnectBusinessCalendarConnection"},"parameters":[{"name":"id","in":"path","required":true,"description":"The calendar connection id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The account was disconnected.","content":{"application/json":{"example":{"success":true,"data":{"id":"9a0b1c2d-3e4f-4a5b-9c6d-7e8f9a0b1c2d","disconnected":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"disconnectBusinessCalendarConnection"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No calendar connection with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The account still holds an active meeting: cancel or move it first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/appointments":{"get":{"operationId":"listBusinessAppointments","summary":"List business appointments","description":"Lists business meetings by start time, oldest first. `from` and `to` keep the meetings that overlap the range (send `from` = now for the upcoming ones).\n\nRequires the `appointments:read` scope.","tags":["Agenda"],"x-required-scope":"appointments:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#listBusinessAppointments"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"from","in":"query","required":false,"description":"Only meetings that end after this time (ISO 8601 with an offset or Z).","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"Only meetings that start before this time.","schema":{"type":"string","format":"date-time"}},{"name":"host_id","in":"query","required":false,"description":"Only this host's meetings.","schema":{"type":"string","format":"uuid"}},{"name":"meeting_type_id","in":"query","required":false,"description":"Only meetings of this type.","schema":{"type":"string","format":"uuid"}},{"name":"contact_id","in":"query","required":false,"description":"Only this contact's meetings.","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Only meetings in this status.","schema":{"type":"string","enum":["confirmed","cancelled","completed","no_show"]}},{"name":"source","in":"query","required":false,"description":"Only meetings booked this way, as their `source` reads: `whatsapp_agent` or `voice_agent` (an AI agent), `dashboard` or `copilot` (the team), `api`.","schema":{"type":"string","maxLength":40}},{"name":"channel_id","in":"query","required":false,"description":"Only meetings booked on this line (the channel the agent answered on).","schema":{"type":"string","format":"uuid"}},{"name":"calendar_sync_status","in":"query","required":false,"description":"Only meetings whose copy in the host's calendar is in this state.","schema":{"type":"string","enum":["failed","pending","synced","skipped"]}},{"name":"needs_outcome","in":"query","required":false,"description":"`true`: only confirmed meetings that are over and have no outcome yet (the Meetings list's Needs outcome). `false`: every other meeting.","schema":{"type":"string","enum":["true","false"]}},{"name":"q","in":"query","required":false,"description":"Search, as the Meetings list does: a phone number, written any way, finds the invitee's; otherwise each word must appear in the invitee's name, company, email or phone, the meeting type or the host.","schema":{"type":"string","maxLength":120}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of meetings.","content":{"application/json":{"example":{"success":true,"data":[{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","meeting_type_id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","meeting_type_name":"Intro call","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","host_name":"Ana López","invitee_name":"Carlos Ruiz","invitee_email":"carlos@example.com","invitee_phone":"+528112345678","invitee_company":"Acme","invitee_notes":null,"starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","status":"confirmed","location_kind":"google_meet","location_details":null,"meeting_url":"https://meet.google.com/abc-defg-hij","source":"dashboard","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","channel_id":null,"call_id":null,"conversation_id":null,"cancellation_reason":null,"calendar_sync_status":"synced","calendar_sync_error":null,"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"bookBusinessAppointment","summary":"Book a business appointment","description":"Books a meeting at a start time from `GET /v1/business/availability`, through the engine that validates the dashboard's and the agents' bookings: the time must still be free. Without `host_id`, the least busy host who can take the meeting type is chosen.\n\nRequires the `appointments:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nWhen the host's Google or Outlook calendar is connected, the meeting is written there (with a Meet link for `google_meet`) and the invitee gets the calendar's invitation; a calendar that fails never undoes the change (`calendar_sync_status`, `calendar_sync_error`).\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Agenda"],"x-required-scope":"appointments:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#bookBusinessAppointment"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"meeting_type_id":{"description":"The meeting type.","type":"string","format":"uuid"},"slot_start":{"description":"The start time: a slot's `start` from the availability search.","type":"string","format":"date-time"},"invitee_name":{"description":"Who the meeting is with.","type":"string","minLength":1,"maxLength":200},"invitee_phone":{"description":"Their phone with the country code (at least 10 digits), e.g. +528112345678: reminders, the contact link and WhatsApp notes use it.","type":"string","maxLength":40},"invitee_email":{"description":"Their email: the host's connected calendar sends them the invitation.","type":["string","null"],"format":"email","maxLength":320},"invitee_company":{"description":"Their company.","type":["string","null"],"maxLength":200},"invitee_notes":{"description":"Notes for the host.","type":["string","null"],"maxLength":2000},"host_id":{"description":"Book this host. Omitted or null: any host who can take the meeting type (least busy first).","type":["string","null"],"format":"uuid"},"override_validation":{"description":"Book even outside the hosts' hours, days off and notice, without checking their connected calendars. A host is never double-booked.","type":"boolean","default":false}},"required":["meeting_type_id","slot_start","invitee_name","invitee_phone"]},"example":{"meeting_type_id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","slot_start":"2026-10-20T16:00:00Z","invitee_name":"Carlos Ruiz","invitee_phone":"+528112345678","invitee_email":"carlos@example.com","invitee_company":"Acme"}}}},"responses":{"201":{"description":"The booked meeting.","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","meeting_type_id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","meeting_type_name":"Intro call","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","host_name":"Ana López","invitee_name":"Carlos Ruiz","invitee_email":"carlos@example.com","invitee_phone":"+528112345678","invitee_company":"Acme","invitee_notes":null,"starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","status":"confirmed","location_kind":"google_meet","location_details":null,"meeting_url":"https://meet.google.com/abc-defg-hij","source":"dashboard","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","channel_id":null,"call_id":null,"conversation_id":null,"cancellation_reason":null,"calendar_sync_status":"synced","calendar_sync_error":null,"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A field the booking needs is missing or invalid (the message names it).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: The meeting type doesn't exist or is archived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The time can't be booked: it is taken, outside the hosts' hours or notice, a host's connected calendar couldn't be checked, or no host can take the meeting type (`host_id` is not an active host who takes it). The message says which.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/appointments/{id}":{"get":{"operationId":"getBusinessAppointment","summary":"Get a business appointment","description":"Returns one business meeting, in any status.\n\nRequires the `appointments:read` scope.","tags":["Agenda"],"x-required-scope":"appointments:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#getBusinessAppointment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The meeting.","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","meeting_type_id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","meeting_type_name":"Intro call","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","host_name":"Ana López","invitee_name":"Carlos Ruiz","invitee_email":"carlos@example.com","invitee_phone":"+528112345678","invitee_company":"Acme","invitee_notes":null,"starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","status":"confirmed","location_kind":"google_meet","location_details":null,"meeting_url":"https://meet.google.com/abc-defg-hij","source":"dashboard","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","channel_id":null,"call_id":null,"conversation_id":null,"cancellation_reason":null,"calendar_sync_status":"synced","calendar_sync_error":null,"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateBusinessAppointment","summary":"Reschedule, cancel or update a business appointment","description":"Makes one change per request: a new time (`slot_start`, with `duration_minutes`, `host_id`, `notify_invitee` and `override_validation`), a status (`cancelled` with `cancellation_reason`, or `completed` / `no_show` once the meeting has started), or the invitee's details. A move keeps the meeting's host unless you name another in `host_id`; `host_id: null` lets any host who can take the meeting type have it when its host isn't free.\n\nRequires the `appointments:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nWhen the host's Google or Outlook calendar is connected, the meeting is written there (with a Meet link for `google_meet`) and the invitee gets the calendar's invitation; a calendar that fails never undoes the change (`calendar_sync_status`, `calendar_sync_error`).\n\nThe change runs through the dashboard's own rules, as the member who created the key (an owner acts as an admin). Owners, admins, builders and operators can change business scheduling; a key made by a viewer can only read. Reseller keys (rk_…) can't make this change.","tags":["Agenda"],"x-required-scope":"appointments:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#updateBusinessAppointment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"slot_start":{"description":"Move the meeting to this start time (find one with the availability search and `appointment_id`).","type":"string","format":"date-time"},"duration_minutes":{"description":"With slot_start: a new length. Omitted: the meeting keeps its own length.","type":"integer","minimum":5,"maximum":480},"host_id":{"description":"With slot_start: move it to this host. Omitted: it keeps its host. null: its host when free, else any host who can take the meeting type.","type":["string","null"],"format":"uuid"},"notify_invitee":{"description":"With slot_start: send the invitee a WhatsApp note with the new time (`notification` says whether it went out).","type":"boolean","default":false},"override_validation":{"description":"With slot_start: move it even outside the host's hours, without checking their connected calendars (never onto another meeting of the host).","type":"boolean","default":false},"status":{"description":"`cancelled` calls off a confirmed meeting (its calendar event is removed); `completed` and `no_show` record how a started meeting went. Each frees the host's time and can't be undone.","type":"string","enum":["cancelled","completed","no_show"]},"cancellation_reason":{"description":"With status cancelled: why.","type":["string","null"],"maxLength":500},"invitee_name":{"description":"Who the meeting is with.","type":"string","minLength":1,"maxLength":200},"invitee_phone":{"description":"Their phone with the country code (at least 10 digits), e.g. +528112345678: reminders, the contact link and WhatsApp notes use it.","type":"string","maxLength":40},"invitee_email":{"description":"Their email: the host's connected calendar sends them the invitation.","type":["string","null"],"format":"email","maxLength":320},"invitee_company":{"description":"Their company.","type":["string","null"],"maxLength":200},"invitee_notes":{"description":"Notes for the host.","type":["string","null"],"maxLength":2000}}},"example":{"slot_start":"2026-10-21T17:00:00Z","notify_invitee":true}}}},"responses":{"200":{"description":"The updated meeting. A move with `notify_invitee` also returns `notification`; a move to another host reports `previous_host_calendar_cleanup`, and a cancel `calendar_cleanup` (`removed`, `skipped` or `failed`).","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","meeting_type_id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","meeting_type_name":"Intro call","host_id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","host_name":"Ana López","invitee_name":"Carlos Ruiz","invitee_email":"carlos@example.com","invitee_phone":"+528112345678","invitee_company":"Acme","invitee_notes":null,"starts_at":"2026-10-21T17:00:00+00:00","ends_at":"2026-10-21T17:30:00+00:00","timezone":"America/Monterrey","status":"confirmed","location_kind":"google_meet","location_details":null,"meeting_url":"https://meet.google.com/abc-defg-hij","source":"dashboard","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","channel_id":null,"call_id":null,"conversation_id":null,"cancellation_reason":null,"calendar_sync_status":"synced","calendar_sync_error":null,"created_at":"2026-10-12T15:04:05.123456+00:00","updated_at":"2026-10-12T15:04:05.123456+00:00"},"notification":{"status":"sent","reason":null,"message":"WhatsApp note sent with the new time."}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateBusinessAppointment"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The body asks for no change or for more than one, or an option is sent without the change it belongs to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same). `not_found`: Cancelling a meeting that is no longer confirmed (already cancelled, completed or a no-show).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The new time can't be booked (taken, outside the host's hours, a connected calendar couldn't be checked, or the host can't take the meeting type: archived, or not one who takes it), the meeting is not confirmed or has not started yet (completed, no_show), or it changed meanwhile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/appointments/{id}/history":{"get":{"operationId":"getBusinessAppointmentHistory","summary":"Get a business appointment's history","description":"Lists what happened to one meeting, newest first, as its drawer in the dashboard does: booked, rescheduled, cancelled, reopened, its outcome, and its copy in the host's calendar.\n\nRequires the `appointments:read` scope.\n\n`event_type` is `booked`, `rescheduled`, `cancelled`, `reopened`, `completed`, `no_show`, `calendar_synced` or `calendar_sync_failed`. `actor_type` says who made the change: `user` (a team member, named in `actor_name` while they belong to the workspace; nobody's email is shown), `ai` (an agent) or `system`. `from` and `to` hold the fields the change touched.\n\nThe latest 200 changes are listed; `truncated` is true when the meeting has older ones.","tags":["Agenda"],"x-required-scope":"appointments:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#getBusinessAppointmentHistory"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The meeting's changes, newest first.","content":{"application/json":{"example":{"success":true,"data":[{"id":"2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e","event_type":"rescheduled","actor_type":"user","actor_name":"Ana López","source":"dashboard","from":{"starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00"},"to":{"starts_at":"2026-10-21T16:00:00+00:00","ends_at":"2026-10-21T16:30:00+00:00"},"created_at":"2026-10-12T15:04:05.123456+00:00"}],"truncated":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/business/availability":{"get":{"operationId":"findBusinessAvailability","summary":"Find free business meeting times","description":"Returns the free start times for a meeting type from the engine that validates bookings: the hosts' weekly hours, days off, closures, booked meetings, buffers, notice and booking window, and their connected calendars. Searches one day (`date`) or a range (at most 31 days, up to 300 times); with neither, the next 7 days.\n\nRequires the `appointments:read` scope.\n\nRuns as the member who created the key (it reads with their access); reseller keys can't call it.","tags":["Agenda"],"x-required-scope":"appointments:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#findBusinessAvailability"},"parameters":[{"name":"meeting_type_id","in":"query","required":true,"description":"The meeting type.","schema":{"type":"string","format":"uuid"}},{"name":"date","in":"query","required":false,"description":"One day, YYYY-MM-DD, in the workspace's time zone. Don't combine it with a range.","schema":{"type":"string"}},{"name":"range_start","in":"query","required":false,"description":"Start of the range (default: now plus the meeting type's notice).","schema":{"type":"string","format":"date-time"}},{"name":"range_end","in":"query","required":false,"description":"End of the range (default: 7 days after its start).","schema":{"type":"string","format":"date-time"}},{"name":"host_id","in":"query","required":false,"description":"Only this host's times.","schema":{"type":"string","format":"uuid"}},{"name":"appointment_id","in":"query","required":false,"description":"The meeting you want to move: its own time doesn't count as busy and the times have its length. It must be of this meeting type.","schema":{"type":"string","format":"uuid"}},{"name":"language","in":"query","required":false,"description":"The language of each slot's `display`.","schema":{"type":"string","enum":["en","es"],"default":"en"}}],"responses":{"200":{"description":"The free times, each with the host it would be booked with. `warnings` names hosts whose connected calendar couldn't be read (their times show, but booking them is refused until it can be); with no times, `reason` says why, and `code` is `no_eligible_host` or `host_missing` when no host can take the meeting type.","content":{"application/json":{"example":{"success":true,"data":{"timezone":"America/Monterrey","meeting_type":{"id":"8f9a0b1c-2d3e-4f4a-8b5c-6d7e8f9a0b1c","name":"Intro call","duration_minutes":30},"slots":[{"start":"2026-10-20T16:00:00.000Z","end":"2026-10-20T16:30:00.000Z","display":"Tue, Oct 20, 10:00 AM","timezone":"America/Monterrey","host":{"id":"1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f","name":"Ana López"},"available_hosts":2}],"warnings":[],"reason":null,"code":null}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: `date` was sent with a range, the range ends before it starts, or `appointment_id` is a meeting of another type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The workspace doesn't run Business Appointments (it is turned on per workspace). `forbidden`: The member who created the key can't do this (a viewer, or a clinic-only role), is no longer in the workspace, or the key is not a workspace key (sk_…).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: The meeting type doesn't exist or is archived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/appointments":{"get":{"operationId":"listClinicAppointments","summary":"List appointments","description":"Lists the clinic's appointments in start order. Window it with `from` and `to`, and filter by doctor, service, location, room, spot, status, patient phone or contact, who booked it, the line it came in on, whether the patient confirmed it, or whether it awaits its outcome.\n\nRequires the `appointments:read` scope.\n\n`status` is one of `scheduled`, `completed` (attended: someone marked it, or reception booked the visit and it closed automatically a day after its end), `unconfirmed` (an AI agent booked the visit, its time passed and nobody recorded whether the patient came: neither attended nor a no-show), `no_show` or `cancelled`.\n\n`source` says where it was booked: `dashboard` (reception, and this API), `ai_voice`, `ai_whatsapp`, `ai_instagram`, `ai_messenger`, `ai_web_widget`, `ai_imessage` (an AI agent on that channel), `api` or `import`. `service_names` lists every service of a visit booked with several.","tags":["Agenda"],"x-required-scope":"appointments:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#listClinicAppointments"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"from","in":"query","required":false,"description":"Only appointments that start at or after this time.","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"Only appointments that start before this time.","schema":{"type":"string","format":"date-time"}},{"name":"doctor_id","in":"query","required":false,"description":"Only this doctor's appointments.","schema":{"type":"string","format":"uuid"}},{"name":"service_id","in":"query","required":false,"description":"Only appointments for this service.","schema":{"type":"string","format":"uuid"}},{"name":"location_id","in":"query","required":false,"description":"Only appointments at this location.","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Only appointments in this status.","schema":{"type":"string","enum":["scheduled","completed","unconfirmed","no_show","cancelled"]}},{"name":"patient_phone","in":"query","required":false,"description":"Only the patient's appointments: matched by the last 10 digits, however the number was typed.","schema":{"type":"string"}},{"name":"contact_id","in":"query","required":false,"description":"Only the appointments of this contact.","schema":{"type":"string","format":"uuid"}},{"name":"room_id","in":"query","required":false,"description":"Only appointments in this room.","schema":{"type":"string","format":"uuid"}},{"name":"spot_id","in":"query","required":false,"description":"Only appointments on this spot.","schema":{"type":"string","format":"uuid"}},{"name":"source","in":"query","required":false,"description":"Only appointments booked this way, as their `source` reads: `dashboard` (the team), `ai_voice`, `ai_whatsapp`, `ai_instagram`, `ai_messenger`, `ai_web_widget` or `ai_imessage` (an AI agent on that channel), `api`, `import`.","schema":{"type":"string","maxLength":40}},{"name":"channel_id","in":"query","required":false,"description":"Only appointments booked on this line (the channel the agent answered on).","schema":{"type":"string","format":"uuid"}},{"name":"patient_confirmed","in":"query","required":false,"description":"`true`: only appointments the patient confirmed (`confirmed_at` is set); `false`: the others. Not the status `unconfirmed`, which says nobody recorded whether the patient came.","schema":{"type":"string","enum":["true","false"]}},{"name":"awaiting_outcome","in":"query","required":false,"description":"`true`: only appointments nobody recorded an outcome for (`unconfirmed`, or still `scheduled` after their start), as the agenda's Awaiting outcome filter. `false`: every other appointment.","schema":{"type":"string","enum":["true","false"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of appointments.","content":{"application/json":{"example":{"success":true,"data":[{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","status":"scheduled","starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","doctor_name":"Dra. Ana López","service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","service_name":"Consulta dermatológica","service_names":[],"location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","spot_id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","patient":{"name":"María Pérez","phone":"+528112345670","email":null},"contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","reason":"Consulta dermatológica","notes":null,"confirmed_at":null,"source":"dashboard","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":null,"call_id":null,"visit_id":null,"cancelled_at":null,"cancellation_reason":null,"created_at":"2026-10-12T17:20:41.118+00:00","updated_at":"2026-10-12T17:20:41.118+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"bookClinicAppointment","summary":"Book an appointment","description":"Books an appointment through the scheduling engine: working hours, days off, notice and advance limits, buffers, the doctor's, room's and spot's other appointments and the patient's own are checked as when reception books. Pick a free time with GET /clinic/availability first. Book one service (`service_id`), several at the same time (`service_ids`), or several one after another (`sequence`).\n\nRequires the `appointments:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs through the dashboard's own Clinic Scheduling handler and the scheduling engine the AI agents use (the same rules everywhere), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.\n\nWith an `Idempotency-Key`, a retry is the same booking for the scheduling engine too: it answers the appointment it made, even after a lost response.","tags":["Agenda"],"x-required-scope":"appointments:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#bookClinicAppointment"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"service_id":{"description":"The service (one of service_id, service_ids or sequence).","type":"string","format":"uuid"},"service_ids":{"description":"Several services for one visit, all at `starts_at`. Needs `allow_manual_simultaneous_services` in the clinic settings.","type":"array","minItems":2,"maxItems":8,"items":{"description":"A service.","type":"string","format":"uuid"}},"starts_at":{"description":"The start (a slot's `start`); required with service_id or service_ids.","type":"string","format":"date-time"},"doctor_id":{"description":"The doctor for the service (each service without its own component); without it the engine picks a free, qualified one.","type":"string","format":"uuid"},"room_id":{"description":"The room for the service (each service without its own component).","type":"string","format":"uuid"},"spot_id":{"description":"The spot for the service (each service without its own component) (its room follows).","type":"string","format":"uuid"},"components":{"description":"With service_ids: the doctor, room and spot of each service, as a slot's `components` gives them.","type":"array","maxItems":8,"items":{"description":"One service's resources.","type":"object","properties":{"service_id":{"description":"One of service_ids.","type":"string","format":"uuid"},"doctor_id":{"description":"The doctor for this service; without it the engine picks a free, qualified one.","type":"string","format":"uuid"},"room_id":{"description":"The room for this service.","type":"string","format":"uuid"},"spot_id":{"description":"The spot for this service (its room follows).","type":"string","format":"uuid"}},"required":["service_id"]}},"sequence":{"description":"Several services one after another, each with its own start (each starting after the previous ends).","type":"array","minItems":2,"maxItems":12,"items":{"description":"One service of the sequence.","type":"object","properties":{"service_id":{"description":"The service.","type":"string","format":"uuid"},"starts_at":{"description":"Its start.","type":"string","format":"date-time"},"doctor_id":{"description":"The doctor for this service; without it the engine picks a free, qualified one.","type":"string","format":"uuid"},"room_id":{"description":"The room for this service.","type":"string","format":"uuid"},"spot_id":{"description":"The spot for this service (its room follows).","type":"string","format":"uuid"}},"required":["service_id","starts_at"]}},"patient":{"description":"Who the appointment is for.","type":"object","properties":{"name":{"description":"The patient's full name.","type":"string","minLength":1,"maxLength":200},"phone":{"description":"At least 10 digits, ideally E.164 (`+528112345670`): reminders, the agents and the contact record key on it.","type":"string","maxLength":40},"email":{"description":"The patient's email.","type":["string","null"],"format":"email","maxLength":320}},"required":["name","phone"]},"reason":{"description":"The visit's reason (default: the service's name).","type":"string","maxLength":1000},"notes":{"description":"Notes for the team.","type":"string","maxLength":5000},"channel_id":{"description":"The voice or WhatsApp channel the appointment's reminders and follow-ups go out on. `null`: no automatic reminders (for a patient who asked not to get them). Left out: the dashboard's default, the first connected channel with clinic AI enabled (no reminders when there is none).","type":["string","null"],"format":"uuid"},"timezone":{"description":"IANA time zone the engine's messages tell times in.","type":"string","maxLength":64}},"required":["patient"]},"example":{"service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","starts_at":"2026-10-20T10:00:00-06:00","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","spot_id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","patient":{"name":"María Pérez","phone":"+528112345670"}}}}},"responses":{"201":{"description":"The booked appointment (`data`, the first) and every appointment the booking made (`appointments`: one per service).","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","status":"scheduled","starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","doctor_name":"Dra. Ana López","service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","service_name":"Consulta dermatológica","service_names":[],"location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","spot_id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","patient":{"name":"María Pérez","phone":"+528112345670","email":null},"contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","reason":"Consulta dermatológica","notes":null,"confirmed_at":null,"source":"dashboard","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":null,"call_id":null,"visit_id":null,"cancelled_at":null,"cancellation_reason":null,"created_at":"2026-10-12T17:20:41.118+00:00","updated_at":"2026-10-12T17:20:41.118+00:00"},"appointments":[{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","status":"scheduled","starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","doctor_name":"Dra. Ana López","service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","service_name":"Consulta dermatológica","service_names":[],"location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","spot_id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","patient":{"name":"María Pérez","phone":"+528112345670","email":null},"contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","reason":"Consulta dermatológica","notes":null,"confirmed_at":null,"source":"dashboard","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":null,"call_id":null,"visit_id":null,"cancelled_at":null,"cancellation_reason":null,"created_at":"2026-10-12T17:20:41.118+00:00","updated_at":"2026-10-12T17:20:41.118+00:00"}]}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A service is not an active service of yours, `channel_id` is not one of your voice or WhatsApp channels, or a sequence's service starts before the previous one ends.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key. `forbidden`: `service_ids` while the clinic settings don't allow several services at once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The scheduling engine refused the time (outside working hours or a day off, too soon or too far ahead, the doctor, room or spot is taken, the patient already has an appointment then): the message says why. `validation_error`: The Idempotency-Key was used for another booking, or that booking is still being made: retry in a moment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"description":"`internal_error`: The scheduling engine could not confirm the change: read the appointment before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clinic/appointments/{id}":{"get":{"operationId":"getClinicAppointment","summary":"Get an appointment","description":"Returns one appointment.\n\nRequires the `appointments:read` scope.\n\n`status` is one of `scheduled`, `completed` (attended: someone marked it, or reception booked the visit and it closed automatically a day after its end), `unconfirmed` (an AI agent booked the visit, its time passed and nobody recorded whether the patient came: neither attended nor a no-show), `no_show` or `cancelled`.","tags":["Agenda"],"x-required-scope":"appointments:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#getClinicAppointment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The appointment.","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","status":"scheduled","starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","doctor_name":"Dra. Ana López","service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","service_name":"Consulta dermatológica","service_names":[],"location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","spot_id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","patient":{"name":"María Pérez","phone":"+528112345670","email":null},"contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","reason":"Consulta dermatológica","notes":null,"confirmed_at":null,"source":"dashboard","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":null,"call_id":null,"visit_id":null,"cancelled_at":null,"cancellation_reason":null,"created_at":"2026-10-12T17:20:41.118+00:00","updated_at":"2026-10-12T17:20:41.118+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateClinicAppointment","summary":"Update an appointment","description":"Two changes, one per request. `provider_id` alone moves this scheduled appointment to another doctor at the same time (the engine checks the doctor is active, allowed for the service and free). Otherwise, the patient's details, the notes or the confirmation change, for every service of the visit. Moving, cancelling and statuses have their own endpoints.\n\nRequires the `appointments:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler and the scheduling engine the AI agents use (the same rules everywhere), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.\n\nServices booked together for one patient form a visit (`visit_id`): moving, cancelling or setting the status of one of its appointments does it for the whole visit.","tags":["Agenda"],"x-required-scope":"appointments:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#updateClinicAppointment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"provider_id":{"description":"The doctor to move the appointment to. Sent alone: no other field goes with it.","type":"string","format":"uuid"},"patient":{"description":"The patient's details to change.","type":"object","properties":{"name":{"description":"Full name.","type":"string","minLength":1,"maxLength":200},"phone":{"description":"At least 10 digits.","type":"string","maxLength":40},"email":{"description":"null clears it.","type":["string","null"],"format":"email","maxLength":320}}},"notes":{"description":"Notes for the team; null clears them.","type":["string","null"],"maxLength":5000},"confirmed":{"description":"The patient confirmed they will come (true), or not yet (false).","type":"boolean"}}},"example":{"provider_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7"}}}},"responses":{"200":{"description":"The appointment as stored after the change. A doctor change answers the scheduling engine's record of the row; a details change answers the appointment as GET shows it.","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","status":"scheduled","starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","doctor_name":"Dra. Ana López","service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","service_name":"Consulta dermatológica","service_names":[],"location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","spot_id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","patient":{"name":"María Pérez","phone":"+528112345670","email":null},"contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","reason":"Consulta dermatológica","notes":null,"confirmed_at":"2026-10-12T17:20:41.118+00:00","source":"dashboard","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":null,"call_id":null,"visit_id":null,"cancelled_at":null,"cancellation_reason":null,"created_at":"2026-10-12T17:20:41.118+00:00","updated_at":"2026-10-12T17:20:41.118+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicAppointment"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The id is not a UUID with provider_id, or provider_id comes with other fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No such appointment or doctor in your workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: A doctor change for an appointment that is not scheduled or uses no staff, to an inactive doctor, or refused by the engine (the doctor is not free or not allowed for the service: the message says why); or the patient already has another appointment at that time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"description":"`internal_error`: The scheduling engine could not confirm the change: read the appointment before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clinic/appointments/{id}/history":{"get":{"operationId":"getClinicAppointmentHistory","summary":"Get an appointment's history","description":"Lists what happened to one appointment, newest first, as its page in the dashboard does: booked, rescheduled, cancelled, confirmed, its doctor, services, patient details, notes and status, and the reminders sent.\n\nRequires the `appointments:read` scope.\n\n`event_type` is `booked`, `rescheduled`, `cancelled`, `confirmed`, `status_changed`, `completed`, `no_show`, `notes_updated`, `patient_updated`, `services_updated`, `provider_changed` or `reminder_sent`. `actor_type` says who made the change: `user` (a team member, named in `actor_name` while they belong to the workspace; nobody's email is shown), `ai` (an agent) or `system`. `from` and `to` hold the fields the change touched; `channel_id`, `conversation_id` and `call_id` say where an agent's change came from.\n\nThe latest 200 changes are listed; `truncated` is true when the appointment has older ones.","tags":["Agenda"],"x-required-scope":"appointments:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#getClinicAppointmentHistory"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The appointment's changes, newest first.","content":{"application/json":{"example":{"success":true,"data":[{"id":"3c4d5e6f-7a8b-4c9d-8e0f-2a3b4c5d6e7f","event_type":"booked","summary":"Appointment booked","actor_type":"ai","actor_name":null,"source":"ai_whatsapp","from":null,"to":{"starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:45:00+00:00"},"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13","call_id":null,"created_at":"2026-10-12T17:20:41.118+00:00"}],"truncated":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/appointments/{id}/record":{"get":{"operationId":"getClinicAppointmentRecord","summary":"Get an appointment's patient record","description":"What's on file for one appointment and what's missing: each of the clinic's questions that applies, with its `state` (`answered`, `missing`, `declined` or `pending`), its `value` and who saved it (`saved_by`, the agent or teammate as the name reads now, null when there's none). A patient's question reads the same on all their appointments. Someone else's appointment holds their own answers (`for_contact: false`). `data` is null for a clinic with no questions.\n\nRequires the `appointments:read` scope.","tags":["Agenda"],"x-required-scope":"appointments:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#getClinicAppointmentRecord"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The appointment's record.","content":{"application/json":{"example":{"success":true,"data":{"appointment_id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","patient_name":"Mariana Ejemplo","for_contact":true,"summary":{"total":3,"answered":2,"declined":0,"pending":0,"missing_needed":1,"missing_optional":0},"answers":[{"field_id":"3c4d5e6f-7a8b-4c9d-8e0f-2a3b4c5d6e7f","label":"City","answer_type":"text","options":null,"scope":"patient","needed":false,"state":"answered","value":"San Pedro","source":"ai_chat","saved_by":"Sofía","updated_at":"2026-10-12T17:20:41.118+00:00"}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateClinicAppointmentRecord","summary":"Update an appointment's patient record","description":"Saves answers on the appointment's record, as your team would on its card. Each answer is checked against its question: one that doesn't fit comes back in `refused` with its `reason`, and the others are saved. A question about the patient is saved for all their appointments. This runs as the key's creator.\n\nRequires the `appointments:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Agenda"],"x-required-scope":"appointments:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#updateClinicAppointmentRecord"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"answers":{"description":"1 to 24 answers.","type":"array","items":{"description":"One answer.","type":"object","properties":{"field_id":{"description":"The question.","type":"string","format":"uuid"},"state":{"description":"`answered` by default. `declined`: the patient would rather not say. `cleared`: takes the answer off the record.","type":"string","enum":["answered","declined","cleared"]},"value":{"description":"The answer, required with `answered`: a date as YYYY-MM-DD, a number as a plain number, a choice as one of its options.","type":"string","maxLength":2000}},"required":["field_id"]}}},"required":["answers"]},"example":{"answers":[{"field_id":"3c4d5e6f-7a8b-4c9d-8e0f-2a3b4c5d6e7f","value":"San Pedro"}]}}}},"responses":{"200":{"description":"The record as it stands, and what wasn't saved.","content":{"application/json":{"example":{"success":true,"data":null,"refused":[{"field_id":"3c4d5e6f-7a8b-4c9d-8e0f-2a3b4c5d6e7f","reason":"not_a_date"}]}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateClinicAppointmentRecord"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/appointments/{id}/reschedule":{"post":{"operationId":"rescheduleClinicAppointment","summary":"Reschedule an appointment","description":"Moves a scheduled appointment to a new start through the scheduling engine, as the calendar's drag does. With `doctor_id` it also changes doctor; without it the engine keeps an eligible doctor (and replaces one who went inactive or lost the service). `notify_patient` sends the patient a WhatsApp note with the new time.\n\nRequires the `appointments:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler and the scheduling engine the AI agents use (the same rules everywhere), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.\n\nServices booked together for one patient form a visit (`visit_id`): moving, cancelling or setting the status of one of its appointments does it for the whole visit.","tags":["Agenda"],"x-required-scope":"appointments:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#rescheduleClinicAppointment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"starts_at":{"description":"The new start.","type":"string","format":"date-time"},"doctor_id":{"description":"The doctor at the new time (default: the engine keeps an eligible one).","type":"string","format":"uuid"},"room_id":{"description":"The room at the new time (single-service appointments).","type":"string","format":"uuid"},"spot_id":{"description":"The spot at the new time (single-service appointments).","type":"string","format":"uuid"},"duration_minutes":{"description":"A new length (single-service appointments; default: it keeps its length).","type":"integer","minimum":5,"maximum":480},"notify_patient":{"description":"Send the patient a WhatsApp note with the new time, in their conversation (within the channel's rules).","type":"boolean","default":false}},"required":["starts_at"]},"example":{"starts_at":"2026-10-21T11:00:00-06:00","notify_patient":true}}}},"responses":{"200":{"description":"The moved appointment. With notify_patient, `notification` says what became of the note: `status` `sent`, `skipped` or `failed`, with `reason` and `message`; the move stands either way.","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","status":"scheduled","starts_at":"2026-10-21T17:00:00+00:00","ends_at":"2026-10-21T17:30:00+00:00","timezone":"America/Monterrey","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","doctor_name":"Dra. Ana López","service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","service_name":"Consulta dermatológica","service_names":[],"location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","spot_id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","patient":{"name":"María Pérez","phone":"+528112345670","email":null},"contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","reason":"Consulta dermatológica","notes":null,"confirmed_at":null,"source":"dashboard","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":null,"call_id":null,"visit_id":null,"cancelled_at":null,"cancellation_reason":null,"created_at":"2026-10-12T17:20:41.118+00:00","updated_at":"2026-10-12T17:20:41.118+00:00"},"notification":{"status":"sent","reason":null,"message":"The patient was told the new time on WhatsApp.","conversation_id":"5f0c2b1e-8d3a-4c6e-9b7f-2a4d6e8c0b13"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"rescheduleClinicAppointment"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The appointment is not scheduled. `validation_error`: The scheduling engine refused the time (outside working hours or a day off, too soon or too far ahead, the doctor, room or spot is taken, the patient already has an appointment then): the message says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"description":"`internal_error`: The scheduling engine could not confirm the change: read the appointment before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clinic/appointments/{id}/cancel":{"post":{"operationId":"cancelClinicAppointment","summary":"Cancel an appointment","description":"Cancels a scheduled appointment and records the reason in its history; its time is free again at once. Cancelling a cancelled appointment changes nothing. A past visit is not cancelled: set its status.\n\nRequires the `appointments:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler and the scheduling engine the AI agents use (the same rules everywhere), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.\n\nServices booked together for one patient form a visit (`visit_id`): moving, cancelling or setting the status of one of its appointments does it for the whole visit.\n\nThe body is optional.","tags":["Agenda"],"x-required-scope":"appointments:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#cancelClinicAppointment"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"description":"Why (kept in the appointment's history).","type":"string","maxLength":500}}},"example":{"reason":"La paciente pidió cancelar."}}}},"responses":{"200":{"description":"The cancelled appointment.","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","status":"cancelled","starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","doctor_name":"Dra. Ana López","service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","service_name":"Consulta dermatológica","service_names":[],"location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","spot_id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","patient":{"name":"María Pérez","phone":"+528112345670","email":null},"contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","reason":"Consulta dermatológica","notes":null,"confirmed_at":null,"source":"dashboard","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":null,"call_id":null,"visit_id":null,"cancelled_at":"2026-10-12T17:20:41.118+00:00","cancellation_reason":"La paciente pidió cancelar.","created_at":"2026-10-12T17:20:41.118+00:00","updated_at":"2026-10-12T17:20:41.118+00:00"}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"cancelClinicAppointment"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The appointment is not scheduled (it already took place).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/appointments/{id}/status":{"post":{"operationId":"setClinicAppointmentStatus","summary":"Set an appointment's status","description":"Records how a visit went: `completed` (attended) or `no_show`; `unconfirmed` once a visit has ended and nobody knows whether the patient came; or `scheduled` to undo one. The change and its history entry are one write.\n\nRequires the `appointments:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs through the dashboard's own Clinic Scheduling handler and the scheduling engine the AI agents use (the same rules everywhere), acting as the member who created the key: their role, at most admin, must allow it. It is recorded as the key's.\n\nServices booked together for one patient form a visit (`visit_id`): moving, cancelling or setting the status of one of its appointments does it for the whole visit.\n\n`status` is one of `scheduled`, `completed` (attended: someone marked it, or reception booked the visit and it closed automatically a day after its end), `unconfirmed` (an AI agent booked the visit, its time passed and nobody recorded whether the patient came: neither attended nor a no-show), `no_show` or `cancelled`.","tags":["Agenda"],"x-required-scope":"appointments:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#setClinicAppointmentStatus"},"parameters":[{"name":"id","in":"path","required":true,"description":"The appointment id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"description":"The new status (cancelling has its own endpoint).","type":"string","enum":["scheduled","completed","no_show","unconfirmed"]}},"required":["status"]},"example":{"status":"completed"}}}},"responses":{"200":{"description":"The appointment with its new status.","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1d2c3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e","status":"completed","starts_at":"2026-10-20T16:00:00+00:00","ends_at":"2026-10-20T16:30:00+00:00","timezone":"America/Monterrey","doctor_id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","doctor_name":"Dra. Ana López","service_id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","service_name":"Consulta dermatológica","service_names":[],"location_id":"8c2e4a6b-0d1f-4a3c-9e5b-7d9f1b3c5e70","room_id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","spot_id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","patient":{"name":"María Pérez","phone":"+528112345670","email":null},"contact_id":"9595b646-ab97-4485-b1b8-e8b58dfcdd19","reason":"Consulta dermatológica","notes":null,"confirmed_at":null,"source":"dashboard","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","conversation_id":null,"call_id":null,"visit_id":null,"cancelled_at":null,"cancellation_reason":null,"created_at":"2026-10-12T17:20:41.118+00:00","updated_at":"2026-10-12T17:20:41.118+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"setClinicAppointmentStatus"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: Changes act as the member who created the key: their role can't make this change in the dashboard, they are no longer in the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No appointment with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: `unconfirmed` before the visit ended, or the appointment is cancelled (a cancelled appointment keeps its status: book a new one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/clinic/availability":{"get":{"operationId":"getClinicAvailability","summary":"Find free times","description":"Returns the free times for a service, or for several at once, in a window of up to 31 days, from the scheduling engine that validates bookings. Each slot names the doctor, room and spot it would take: book it with POST /clinic/appointments.\n\nRequires the `appointments:read` scope.\n\nA slot of several services lists each one's `components` (service, start, end, doctor, room, spot): send them back as `components` with `service_ids` when booking.","tags":["Agenda"],"x-required-scope":"appointments:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/agenda#getClinicAvailability"},"parameters":[{"name":"service_id","in":"query","required":false,"description":"The service (or service_ids).","schema":{"type":"string","format":"uuid"}},{"name":"service_ids","in":"query","required":false,"description":"Several services at the same time: 2 to 8 ids, comma-separated. Each slot then lists its `components`.","schema":{"type":"string"}},{"name":"from","in":"query","required":true,"description":"The window's start.","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":true,"description":"The window's end (at most 31 days after from).","schema":{"type":"string","format":"date-time"}},{"name":"doctor_id","in":"query","required":false,"description":"Only this doctor's times.","schema":{"type":"string","format":"uuid"}},{"name":"location_id","in":"query","required":false,"description":"Only at this location.","schema":{"type":"string","format":"uuid"}},{"name":"room_id","in":"query","required":false,"description":"Only in this room.","schema":{"type":"string","format":"uuid"}},{"name":"timezone","in":"query","required":false,"description":"IANA time zone each slot's `timezone` is told in.","schema":{"type":"string"}}],"responses":{"200":{"description":"The free times. `requires_confirmation`: the service needs reception's (or an outside provider's) confirmation before it is booked. `search_truncated` appears when a several-services search stopped early (`later-starts`: later times may exist; `some-starts`: some times could not be checked).","content":{"application/json":{"example":{"success":true,"data":{"services":[{"id":"c06c8eaf-4b5d-4e7a-9c9f-1b3d5f7a9cb4","name":"Consulta dermatológica","duration_minutes":30}],"requires_confirmation":false,"slots":[{"start":"2026-10-20T16:00:00.000Z","end":"2026-10-20T16:30:00.000Z","timezone":"America/Monterrey","doctor":{"id":"3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7","name":"Dra. Ana López"},"room":{"id":"ae4a6c8d-2f3b-4c5e-9a7d-9f1b3d5e7a92","name":"Consultorio 1"},"spot":{"id":"bf5b7d9e-3a4c-4d6f-8b8e-0a2c4e6f8ba3","name":"Consultorio 1"}}]}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: The window ends before it starts or is longer than 31 days, or a service is not an active service of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Clinic Scheduling is not enabled for the workspace (NuTalk turns it on per workspace). `forbidden`: A reseller key, or a key whose creator is no longer in the workspace (this read acts as the key's creator).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`validation_error`: The engine can't search this request (for example a doctor who can't take the service): the message says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/test-scenarios":{"get":{"operationId":"listTestScenarios","summary":"List an agent's test scenarios","description":"Lists the agent's test scenarios, newest first. A scenario is a situation and the simulated customers who bring it; its goals and competencies are what a run scores when it names none.\n\nRequires the `testing:read` scope.","tags":["Automated testing"],"x-required-scope":"testing:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#listTestScenarios"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of scenarios.","content":{"application/json":{"example":{"success":true,"data":[{"id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","agent_id":"550e8400-e29b-41d4-a716-446655440000","name":"Price objection","description":"A customer asks how much a cleaning costs and pushes back on the price.","channel":"messages","max_turns":12,"conversations_per_customer":1,"pass_threshold":70,"required_keywords":[],"customer_styles":["friendly","hurry"],"customers":[{"name":"Ana","goal":"Find out the price of a dental cleaning and book only if it is under 800 MXN.","personality":"Direct, a little impatient","background":"Has not been to a dentist in two years.","language":"es-MX","objections":["It is cheaper across the street."],"pains":["Sensitive gums"],"gains":["An appointment this week"],"demographics":{}}],"checks":[{"id":"c1","text":"Gives the price from the price list","required":true},{"id":"c2","text":"Offers to book a time","required":false}],"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","weight":1,"required":true}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","weight":1,"required":false,"pass_level":null}],"created_at":"2026-10-12T17:40:11.402911+00:00","updated_at":"2026-10-12T17:40:11.402911+00:00"}],"pagination":{"total":1,"limit":10,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createTestScenario","summary":"Create a test scenario","description":"Writes a test scenario for the agent, with the dashboard's rules: only this agent's goals and competencies can be linked, an archived agent takes no new scenario, and the scenario is saved with its links at once or not at all. A new scenario runs on messages, one conversation per customer, unless you say otherwise.\n\nRequires the `testing:write` scope.\n\nThe change runs Automated testing's own rules as the member who created the key: the key needs the scope and that member a role that writes tests (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#createTestScenario"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The scenario's name.","type":"string","minLength":1,"maxLength":200},"description":{"description":"The situation the customers bring.","type":["string","null"],"maxLength":5000},"customers":{"description":"The simulated customers who bring the situation. A run needs at least one.","type":"array","maxItems":50,"items":{"description":"A simulated customer.","type":"object","properties":{"name":{"description":"What the customer is called.","type":"string","maxLength":200},"goal":{"description":"What they want from the conversation: the simulated customer pursues it.","type":"string","maxLength":2000},"personality":{"description":"How they talk, e.g. `direct, a little impatient`.","type":"string","maxLength":200},"background":{"description":"Who they are and their situation.","type":["string","null"],"maxLength":4000},"language":{"description":"The language they write in (`es-MX`); default the agent's.","type":["string","null"],"maxLength":35},"objections":{"description":"What they push back with.","type":"array","maxItems":20,"items":{"description":"One item.","type":"string","maxLength":500}},"pains":{"description":"Their problems.","type":"array","maxItems":20,"items":{"description":"One item.","type":"string","maxLength":500}},"gains":{"description":"What would win them.","type":"array","maxItems":20,"items":{"description":"One item.","type":"string","maxLength":500}},"demographics":{"description":"Any other facts about them, kept as sent.","type":"object","additionalProperties":true}},"required":["name","goal"]}},"max_turns":{"description":"The most turns a conversation takes (default 15).","type":"integer","minimum":1,"maximum":30},"conversations_per_customer":{"description":"Conversations each customer has with each version in a run.","type":"integer","default":1,"minimum":1,"maximum":5},"channel":{"description":"Where the conversations happen. Calls can't be tested yet: a run of a `calls` scenario is refused.","type":"string","enum":["messages","calls"],"default":"messages"},"pass_threshold":{"description":"The overall score a conversation needs to pass.","type":"number","default":70,"minimum":1,"maximum":100},"required_keywords":{"description":"Words the agent must say.","type":"array","maxItems":50,"items":{"description":"A word or phrase.","type":"string","maxLength":100}},"goals":{"description":"The agent's goals a run scores when it names none.","type":"array","maxItems":50,"items":{"description":"A goal of this agent.","type":"object","properties":{"goal_id":{"description":"A goal of the agent (`GET /v1/goals?agent_id=`).","type":"string","format":"uuid"},"weight":{"description":"How much the item counts, above 0 and at most 10.","type":"number","default":1,"minimum":0,"maximum":10},"required":{"description":"The goal must pass for a conversation to pass; false: it counts in the score only. Left out: a link the scenario has keeps what it had, a new one defaults to true.","type":"boolean"}},"required":["goal_id"]}},"competencies":{"description":"The agent's competencies a run scores when it names none.","type":"array","maxItems":50,"items":{"description":"A competency of this agent.","type":"object","properties":{"competency_id":{"description":"A competency of the agent.","type":"string","format":"uuid"},"weight":{"description":"How much the item counts, above 0 and at most 10.","type":"number","default":1,"minimum":0,"maximum":10},"required":{"description":"The competency must pass for a conversation to pass; false: it counts in the score only. Left out: a link the scenario has keeps what it had, a new one defaults to false.","type":"boolean"},"pass_level":{"description":"The level it passes at, by position in its rubric (0 is the lowest, never a passing one); null: the competency's own bar, its middle level. Left out: kept as it was.","type":["integer","null"],"minimum":1,"maximum":3}},"required":["competency_id"]}}},"required":["name"]},"example":{"name":"Price objection","description":"A customer asks how much a cleaning costs and pushes back on the price.","max_turns":12,"customers":[{"name":"Ana","goal":"Find out the price of a dental cleaning and book only if it is under 800 MXN.","personality":"Direct, a little impatient","language":"es-MX","objections":["It is cheaper across the street."]}],"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735"}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee"}]}}}},"responses":{"201":{"description":"The scenario.","content":{"application/json":{"example":{"success":true,"data":{"id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","agent_id":"550e8400-e29b-41d4-a716-446655440000","name":"Price objection","description":"A customer asks how much a cleaning costs and pushes back on the price.","channel":"messages","max_turns":12,"conversations_per_customer":1,"pass_threshold":70,"required_keywords":[],"customer_styles":["friendly","hurry"],"customers":[{"name":"Ana","goal":"Find out the price of a dental cleaning and book only if it is under 800 MXN.","personality":"Direct, a little impatient","background":"Has not been to a dentist in two years.","language":"es-MX","objections":["It is cheaper across the street."],"pains":["Sensitive gums"],"gains":["An appointment this week"],"demographics":{}}],"checks":[{"id":"c1","text":"Gives the price from the price list","required":true},{"id":"c2","text":"Offers to book a time","required":false}],"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","weight":1,"required":true}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","weight":1,"required":false,"pass_level":null}],"created_at":"2026-10-12T17:40:11.402911+00:00","updated_at":"2026-10-12T17:40:11.402911+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A goal or competency is not this agent's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent is archived, so it can't be tested (`details.reason` is `AGENT_ARCHIVED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-scenarios/{id}":{"get":{"operationId":"getTestScenario","summary":"Get a test scenario","description":"Returns one test scenario with its simulated customers and its goal and competency links.\n\nRequires the `testing:read` scope.","tags":["Automated testing"],"x-required-scope":"testing:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#getTestScenario"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test scenario id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The scenario.","content":{"application/json":{"example":{"success":true,"data":{"id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","agent_id":"550e8400-e29b-41d4-a716-446655440000","name":"Price objection","description":"A customer asks how much a cleaning costs and pushes back on the price.","channel":"messages","max_turns":12,"conversations_per_customer":1,"pass_threshold":70,"required_keywords":[],"customer_styles":["friendly","hurry"],"customers":[{"name":"Ana","goal":"Find out the price of a dental cleaning and book only if it is under 800 MXN.","personality":"Direct, a little impatient","background":"Has not been to a dentist in two years.","language":"es-MX","objections":["It is cheaper across the street."],"pains":["Sensitive gums"],"gains":["An appointment this week"],"demographics":{}}],"checks":[{"id":"c1","text":"Gives the price from the price list","required":true},{"id":"c2","text":"Offers to book a time","required":false}],"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","weight":1,"required":true}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","weight":1,"required":false,"pass_level":null}],"created_at":"2026-10-12T17:40:11.402911+00:00","updated_at":"2026-10-12T17:40:11.402911+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No test scenario with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateTestScenario","summary":"Update a test scenario","description":"Changes the fields you send. `customers`, `goals` and `competencies` each replace the whole list; the scenario and its links are saved at once or not at all. Runs that already ran keep the customers they met.\n\nRequires the `testing:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs Automated testing's own rules as the member who created the key: the key needs the scope and that member a role that writes tests (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#updateTestScenario"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test scenario id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The scenario's name.","type":"string","minLength":1,"maxLength":200},"description":{"description":"The situation the customers bring.","type":["string","null"],"maxLength":5000},"customers":{"description":"The simulated customers (replaces the whole list: send every customer to keep).","type":"array","maxItems":50,"items":{"description":"A simulated customer.","type":"object","properties":{"name":{"description":"What the customer is called.","type":"string","maxLength":200},"goal":{"description":"What they want from the conversation: the simulated customer pursues it.","type":"string","maxLength":2000},"personality":{"description":"How they talk, e.g. `direct, a little impatient`.","type":"string","maxLength":200},"background":{"description":"Who they are and their situation.","type":["string","null"],"maxLength":4000},"language":{"description":"The language they write in (`es-MX`); default the agent's.","type":["string","null"],"maxLength":35},"objections":{"description":"What they push back with.","type":"array","maxItems":20,"items":{"description":"One item.","type":"string","maxLength":500}},"pains":{"description":"Their problems.","type":"array","maxItems":20,"items":{"description":"One item.","type":"string","maxLength":500}},"gains":{"description":"What would win them.","type":"array","maxItems":20,"items":{"description":"One item.","type":"string","maxLength":500}},"demographics":{"description":"Any other facts about them, kept as sent.","type":"object","additionalProperties":true}},"required":["name","goal"]}},"max_turns":{"description":"The most turns a conversation takes.","type":"integer","minimum":1,"maximum":30},"conversations_per_customer":{"description":"Conversations each customer has with each version in a run.","type":"integer","minimum":1,"maximum":5},"channel":{"description":"Where the conversations happen. Calls can't be tested yet: a run of a `calls` scenario is refused.","type":"string","enum":["messages","calls"]},"pass_threshold":{"description":"The overall score a conversation needs to pass.","type":"number","minimum":1,"maximum":100},"required_keywords":{"description":"Words the agent must say.","type":"array","maxItems":50,"items":{"description":"A word or phrase.","type":"string","maxLength":100}},"goals":{"description":"The agent's goals a run scores when it names none (replaces the list).","type":"array","maxItems":50,"items":{"description":"A goal of this agent.","type":"object","properties":{"goal_id":{"description":"A goal of the agent (`GET /v1/goals?agent_id=`).","type":"string","format":"uuid"},"weight":{"description":"How much the item counts, above 0 and at most 10.","type":"number","default":1,"minimum":0,"maximum":10},"required":{"description":"The goal must pass for a conversation to pass; false: it counts in the score only. Left out: a link the scenario has keeps what it had, a new one defaults to true.","type":"boolean"}},"required":["goal_id"]}},"competencies":{"description":"The agent's competencies a run scores when it names none (replaces the list).","type":"array","maxItems":50,"items":{"description":"A competency of this agent.","type":"object","properties":{"competency_id":{"description":"A competency of the agent.","type":"string","format":"uuid"},"weight":{"description":"How much the item counts, above 0 and at most 10.","type":"number","default":1,"minimum":0,"maximum":10},"required":{"description":"The competency must pass for a conversation to pass; false: it counts in the score only. Left out: a link the scenario has keeps what it had, a new one defaults to false.","type":"boolean"},"pass_level":{"description":"The level it passes at, by position in its rubric (0 is the lowest, never a passing one); null: the competency's own bar, its middle level. Left out: kept as it was.","type":["integer","null"],"minimum":1,"maximum":3}},"required":["competency_id"]}}}},"example":{"max_turns":15,"conversations_per_customer":2}}}},"responses":{"200":{"description":"The updated scenario.","content":{"application/json":{"example":{"success":true,"data":{"id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","agent_id":"550e8400-e29b-41d4-a716-446655440000","name":"Price objection","description":"A customer asks how much a cleaning costs and pushes back on the price.","channel":"messages","max_turns":15,"conversations_per_customer":2,"pass_threshold":70,"required_keywords":[],"customer_styles":["friendly","hurry"],"customers":[{"name":"Ana","goal":"Find out the price of a dental cleaning and book only if it is under 800 MXN.","personality":"Direct, a little impatient","background":"Has not been to a dentist in two years.","language":"es-MX","objections":["It is cheaper across the street."],"pains":["Sensitive gums"],"gains":["An appointment this week"],"demographics":{}}],"checks":[{"id":"c1","text":"Gives the price from the price list","required":true},{"id":"c2","text":"Offers to book a time","required":false}],"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","weight":1,"required":true}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","weight":1,"required":false,"pass_level":null}],"created_at":"2026-10-12T17:40:11.402911+00:00","updated_at":"2026-10-12T17:52:40.551827+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateTestScenario"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: No field to change was sent, or a goal or competency is not this agent's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No test scenario with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteTestScenario","summary":"Delete a test scenario","description":"Deletes the scenario and its simulated customers. The runs it ran stay readable with `GET /v1/test-runs/:id`, without their scenario.\n\nRequires the `testing:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs Automated testing's own rules as the member who created the key: the key needs the scope and that member a role that writes tests (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#deleteTestScenario"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test scenario id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The scenario was deleted.","content":{"application/json":{"example":{"success":true,"data":{"id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteTestScenario"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No test scenario with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-scenarios/{id}/runs":{"post":{"operationId":"startTestRun","summary":"Run a test scenario","description":"Starts a test run of the scenario: every simulated customer holds a conversation with the agent version (by default the deployed one), or with each of up to four versions to compare them on the same customers, and the evaluator grades each conversation with the scenario's rubric: its own checks, which always count, and the goals and competencies you name (by default the ones it imported). A conversation passes when every must-pass criterion passes. The run keeps that rubric, so later edits never change its grades. Nothing reaches a real customer and nothing is booked. A workspace's first 50 finished test conversations are free; each one after that uses credits at its plan's test conversation price, charged when it finishes. Answers the run with its conversations pending; follow it with `GET /v1/test-runs/:id`.\n\nRequires the `testing:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nA run takes a few minutes: each conversation runs and is scored in the background. Poll `GET /v1/test-runs/:id` until `status` is `completed`, `partial` or `failed`.\n\nThe run starts as the member who created the key: owners, admins, builders and operators run tests. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#startTestRun"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test scenario id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_version_ids":{"description":"The agent versions to test, each once (`GET /v1/agents/:id` lists them). Two to four compare them: the run then names a winner. Default: the deployed version.","type":"array","minItems":1,"maxItems":4,"items":{"description":"A version of the scenario's agent.","type":"string","format":"uuid"}},"conversations_per_customer":{"description":"Conversations each customer has with each version. Default: the scenario's.","type":"integer","minimum":1,"maximum":5},"channel_id":{"description":"One of the agent's messaging channels: the run applies its settings (scheduling, language). Default: the agent's oldest messaging channel.","type":"string","format":"uuid"},"goal_ids":{"description":"The agent's goals to score. With `competency_ids`, at least one in all; both absent: the scenario's.","type":"array","maxItems":50,"items":{"description":"A goal of the agent.","type":"string","format":"uuid"}},"competency_ids":{"description":"The agent's competencies to score.","type":"array","maxItems":50,"items":{"description":"A competency of the agent.","type":"string","format":"uuid"}},"rerun_of":{"description":"An earlier run of this scenario: this run meets the same customers it met.","type":"string","format":"uuid"}}},"example":{"agent_version_ids":["2ea87934-6e03-4db7-80f2-ac753b724c82","c5c0810c-4614-4f0d-927d-62e587dad45e"],"goal_ids":["7b7b8def-a340-4bfd-b164-779fa5843735"]}}}},"responses":{"201":{"description":"The run, started.","content":{"application/json":{"example":{"success":true,"data":{"id":"6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d","status":"running","error":null,"agent_id":"550e8400-e29b-41d4-a716-446655440000","scenario":{"id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","name":"Price objection","description":"A customer asks how much a cleaning costs and pushes back on the price."},"variants":[{"key":"A","agent_version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.3"}],"results":[{"variant":"A","conversations":3,"open":3,"completed":0,"failed":0,"passed":0,"preferred":null,"average_turns":null}],"winner":null,"rubric":[{"id":"c1","kind":"check","name":"Gives the price from the price list"},{"id":"7b7b8def-a340-4bfd-b164-779fa5843735","kind":"goal","name":"Book qualified meeting"},{"id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","kind":"competency","name":"Objection handling"}],"conversations":[{"id":"8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f","variant":"A","customer":{"id":"1e2f3a4b-5c6d-4e7f-8a9b-0c1d2e3f4a5c","name":"Ana"},"status":"pending","error":null,"passed":null,"score":null,"criteria":null,"summary":null,"goals":[],"competencies":[],"transcript":[]}],"created_at":"2026-10-12T17:41:03.118204+00:00","completed_at":null,"updated_at":"2026-10-12T17:41:03.118204+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The scenario is a `calls` scenario, has no customer, would run more than 100 conversations or has nothing to score; or a goal, competency or channel is not the agent's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No test scenario with this id in your workspace (ids of other workspaces answer the same). `not_found`: An agent version is not the agent's, or `rerun_of` is not a run of this scenario.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent is archived, so it can't be tested (`details.reason` is `AGENT_ARCHIVED`). `conflict`: `agent_version_ids` is absent and the agent has no deployed version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"description":"`internal_error`: The run could not start; it is kept as failed with the reason (`details.test_run_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/agents/{id}/test-runs":{"get":{"operationId":"listTestRuns","summary":"List an agent's test runs","description":"Lists the agent's test runs, newest first, with each run's status and how many of its conversations are open, finished, failed and passed.\n\nRequires the `testing:read` scope.\n\n`status`: `running` while a conversation is open; `completed` when all finished, `partial` when some failed, `failed` when every one failed or the run could not start (`error` says why).","tags":["Automated testing"],"x-required-scope":"testing:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#listTestRuns"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"scenario_id","in":"query","required":false,"description":"Only the runs of this scenario.","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Only runs in this status.","schema":{"type":"string","enum":["pending","running","completed","partial","failed","cancelled"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of runs.","content":{"application/json":{"example":{"success":true,"data":[{"id":"6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d","name":"Price objection - Oct 12, 2026, 11:41 AM","status":"completed","scenario":{"id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","name":"Price objection"},"variants":[{"key":"A","agent_version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.3"}],"conversations":{"total":3,"open":0,"completed":3,"failed":0,"passed":2},"error":null,"created_at":"2026-10-12T17:41:03.118204+00:00","completed_at":"2026-10-12T17:43:52.007311+00:00"}],"pagination":{"total":1,"limit":10,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-runs/{id}":{"get":{"operationId":"getTestRun","summary":"Get a test run","description":"Returns one test run as Automated testing shows it: each version's results, the winner of a comparison (the most conversations passed, the evaluator's head-to-head breaking a tie), the criteria it was graded with (the scenario's checks, goals and competencies), and every simulated conversation with its transcript, the evaluator's summary, its score and its verdict on each criterion.\n\nRequires the `testing:read` scope.\n\nA conversation's `status` is `pending`, `running` or `evaluating` while it is open, then `completed` or `failed` (`error` says why). `passed` is null until it is scored.\n\n`criteria` holds each criterion's verdict (`pass`, `fail` or `na` when it didn't apply), with the reason and the message or tool call that decided it (numbered as in the transcript: messages of both sides count, an agent's tool calls apart). `passed` is true when no `required` criterion failed; `score` is the share of the criteria that applied which passed, out of 100. Both are null for a conversation graded before rubrics; its `goals` and `competencies` explain it instead.","tags":["Automated testing"],"x-required-scope":"testing:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#getTestRun"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test run id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The run.","content":{"application/json":{"example":{"success":true,"data":{"id":"6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d","status":"completed","error":null,"agent_id":"550e8400-e29b-41d4-a716-446655440000","scenario":{"id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","name":"Price objection","description":"A customer asks how much a cleaning costs and pushes back on the price."},"variants":[{"key":"A","agent_version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.3"}],"results":[{"variant":"A","conversations":3,"open":0,"completed":3,"failed":0,"passed":2,"preferred":null,"average_turns":5.3}],"winner":null,"rubric":[{"id":"c1","kind":"check","name":"Gives the price from the price list"},{"id":"7b7b8def-a340-4bfd-b164-779fa5843735","kind":"goal","name":"Book qualified meeting"},{"id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","kind":"competency","name":"Objection handling"}],"conversations":[{"id":"8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f","variant":"A","customer":{"id":"1e2f3a4b-5c6d-4e7f-8a9b-0c1d2e3f4a5c","name":"Ana"},"status":"completed","error":null,"passed":true,"score":100,"criteria":[{"kind":"check","id":"c1","name":"Gives the price from the price list","required":true,"verdict":"pass","level":null,"reason":"Quoted 750 MXN, the price list's.","evidence":{"kind":"message","number":2}},{"kind":"goal","id":"7b7b8def-a340-4bfd-b164-779fa5843735","name":"Book qualified meeting","required":true,"verdict":"pass","level":null,"reason":"The customer accepted Thursday at 10:00.","evidence":{"kind":"message","number":4}}],"summary":"The agent gave the price, answered the objection with the included check-up and booked Thursday at 10:00.","goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","title":"Book qualified meeting","met":true,"justification":"The customer accepted Thursday at 10:00."}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","name":"Objection handling","level":"proficient","passed":true,"justification":"It acknowledged the price concern and explained what the cleaning includes."}],"transcript":[{"role":"customer","text":"Hola, ¿cuánto cuesta una limpieza dental?","tools":[]},{"role":"agent","text":"¡Hola! La limpieza cuesta 750 MXN e incluye revisión. ¿Te gustaría agendar esta semana?","tools":[]}]}],"created_at":"2026-10-12T17:41:03.118204+00:00","completed_at":"2026-10-12T17:43:52.007311+00:00","updated_at":"2026-10-12T17:43:52.007311+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No test run with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-runs/{id}/evaluate":{"post":{"operationId":"rescoreTestRun","summary":"Score a test run again","description":"Scores the run's finished conversations again (all of them, or the ones you name) with the rubric the run kept, so the evaluator reads them again against the same criteria. To grade with the scenario's rubric as it is now (after rewording a goal, for example), start a new run with `POST /v1/test-scenarios/:id/runs`, with `rerun_of` to meet the same customers. A run that kept no rubric (one from before rubrics) is scored with the goals and competencies as they are now. Each conversation goes back to `evaluating`; its previous scores are replaced once the new scoring is queued, and kept when it can't be.\n\nRequires the `testing:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs Automated testing's own rules as the member who created the key: the key needs the scope and that member a role that writes tests (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#rescoreTestRun"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test run id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"conversation_ids":{"description":"Only these conversations of the run. Default: every finished one.","type":"array","minItems":1,"maxItems":100,"items":{"description":"A conversation id from `GET /v1/test-runs/:id`.","type":"string","format":"uuid"}}}},"example":{"conversation_ids":["8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f"]}}}},"responses":{"200":{"description":"The conversations being scored again; `skipped` lists the ones that couldn't be, with the reason.","content":{"application/json":{"example":{"success":true,"data":{"id":"6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d","rescoring":["8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f"],"skipped":[]}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"rescoreTestRun"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No test run with this id in your workspace (ids of other workspaces answer the same). `not_found`: A conversation is not part of this run (`details.conversation_ids`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The run has no finished conversation, or none could be scored again (`details.skipped` says why for each).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/test-chat":{"post":{"operationId":"sendTestChatMessage","summary":"Try the agent in a test chat","description":"One turn of Try it yourself: you write as a customer and the agent version answers through its real engine (its prompt, knowledge, tools and model), with the tools it used. Nothing is sent or booked and no credit is used; each reply counts against the workspace's free test replies of the day. Send the `chat_id` of an answer to continue that chat.\n\nRequires the `testing:write` scope.\n\nA reply can take up to 90 seconds when the agent uses tools. Tools that would change something (a booking) are simulated (`simulated: true`).\n\nThe turn runs as the member who created the key (owners, admins and builders try agents); the chat appears in the dashboard's Try it yourself history.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#sendTestChatMessage"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"What the customer writes.","type":"string","minLength":1,"maxLength":4096},"agent_version_id":{"description":"The version that answers, any version of the agent. Default: the chat's, else the deployed one.","type":"string","format":"uuid"},"compare_agent_version_id":{"description":"Another version that answers the same messages, side by side (`reply.compare`).","type":["string","null"],"format":"uuid"},"chat_id":{"description":"The `chat_id` of an earlier answer: the turn continues that chat, with its versions.","type":"string","format":"uuid"},"channel_id":{"description":"One of the agent's channels whose settings apply (scheduling, language). Default: chosen for you; a chat keeps its channel.","type":["string","null"],"format":"uuid"}},"required":["message"]},"example":{"message":"Hola, ¿tienen citas el jueves?"}}}},"responses":{"200":{"description":"The agent's reply.","content":{"application/json":{"example":{"success":true,"data":{"chat_id":"2b3c4d5e-6f70-4a8b-9c0d-1e2f3a4b5c6d","channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","message":{"id":"3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f","text":"Hola, ¿tienen citas el jueves?","at":"2026-10-12T18:02:11.204+00:00"},"reply":{"id":"5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b","text":"¡Claro! El jueves tengo libre a las 10:00 y a las 16:30. ¿Cuál prefieres?","at":"2026-10-12T18:02:14.871+00:00","model":null,"latency_ms":3612,"tools":[{"tool":"check_availability","simulated":false,"summary":"2 free times on Thursday"}],"compare":null},"replies_left_today":287}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The engine refused the turn (a clinic agent with no messaging channel to take settings from: `details.reason` `ENGINE_REFUSED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: Automated testing isn't available in this workspace, or the member who created the key can't test (owners, admins and builders).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same). `not_found`: The version, the chat or the channel is not the agent's.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent is archived, so it can't be tested (`details.reason` is `AGENT_ARCHIVED`). `conflict`: The chat tested other versions, is full (160 messages) or runs on another channel (`details.reason`), or the agent has no deployed version to default to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: The workspace used today's free test replies, or sent too many in a minute (`details.reason`: `TEST_REPLIES_LIMIT` or `TEST_REPLIES_BURST`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: The agent's engine did not answer (`details.reason` `ENGINE_FAILED`), or test replies can't be counted right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/agents/{id}/test-run-alls":{"post":{"operationId":"startTestRunAll","summary":"Run all of an agent's scenarios","description":"Runs every test scenario of the agent at once, or the ones named, as Automated testing → Run all does: on its live version, its draft, or both to compare them on the same customers. Each scenario's test is a test run (`started[].test_run_id`); a scenario that can't run is skipped with why (`calls`: calls scenarios run from the dashboard; `empty_rubric`; `no_customers`). At most 200 conversations. A workspace's first 50 finished test conversations are free; each one after that uses credits at its plan's test conversation price, charged when it finishes. Follow it with `GET /v1/test-run-alls/:id`.\n\nRequires the `testing:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nThe change runs Automated testing's own rules as the member who created the key: the key needs the scope and that member a role that writes tests (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nNothing reaches a real customer and nothing is booked.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#startTestRunAll"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"version":{"description":"The live version, the draft, or both (compared).","type":"string","enum":["live","draft","both"]},"customers_per_scenario":{"description":"How many simulated customers each scenario's test meets.","type":"integer","minimum":1,"maximum":5},"scenario_ids":{"description":"Only these scenarios (default: all of them).","type":"array","minItems":1,"maxItems":100,"items":{"description":"A scenario of the agent.","type":"string","format":"uuid"}},"rerun_of":{"description":"The Run all this one runs again (its failed scenarios, when `scenario_ids` names them).","type":"string","format":"uuid"}},"required":["version","customers_per_scenario"]},"example":{"version":"live","customers_per_scenario":3}}}},"responses":{"201":{"description":"Started.","content":{"application/json":{"example":{"success":true,"data":{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","scenarios":6,"conversations":18,"started":[{"scenario_id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","test_run_id":"6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d"}],"skipped":[{"scenario_id":"9f0a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c","name":"Calls in English","reason":"calls"}],"could_not_start":[]}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The agent has no live version or no draft for the version asked, no scenario can run (`details` lists the skipped ones), or it would be over 200 conversations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent, scenario or Run all to run again with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent is archived, so it can't be tested (`details.reason` is `AGENT_ARCHIVED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: The workspace started too many test runs through the API recently: at most 30 an hour and 5 a minute.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"listTestRunAlls","summary":"List an agent's Run alls","description":"The agent's newest Run alls (up to 24), newest first, one row per version each ran (a Run all that compared two versions has two rows with the same `id`): how many scenarios passed, its conversations' average score and the share that passed.\n\nRequires the `testing:read` scope.","tags":["Automated testing"],"x-required-scope":"testing:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#listTestRunAlls"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The Run alls.","content":{"application/json":{"example":{"success":true,"data":[{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","version_key":"A","agent_version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v12","created_at":"2026-10-27T18:20:05.117+00:00","status":"finished","scenarios":6,"scenarios_passed":5,"score":88,"conversations_passed_percent":89}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-run-alls/{id}":{"get":{"operationId":"getTestRunAll","summary":"Get a Run all's results","description":"A Run all's results, as its page shows them, one block per version it ran: how many scenarios passed, each scenario's test run (`GET /v1/test-runs/:id` has its grading) and conversations, and What to fix: the changes the judge proposed, ranked by the failed conversations each explains. A fix the team hid is marked `dismissed`. `status` is `running` until every test ends.\n\nRequires the `testing:read` scope.","tags":["Automated testing"],"x-required-scope":"testing:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#getTestRunAll"},"parameters":[{"name":"id","in":"path","required":true,"description":"The Run all id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The Run all.","content":{"application/json":{"example":{"success":true,"data":{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","agent_id":"550e8400-e29b-41d4-a716-446655440000","version":"live","customers_per_scenario":3,"rerun_of":null,"status":"finished","created_at":"2026-10-27T18:20:05.117+00:00","finished_at":"2026-10-27T18:31:40.902+00:00","versions":[{"key":"A","agent_version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","label":"v12","scenarios":6,"scenarios_passed":5,"scenarios_running":0,"conversations":18,"conversations_passed":16,"score":88,"results":[{"test_run_id":"6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d","scenario_id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","name":"Books a cleaning","result":"failed","passed":1,"total":3,"score":71,"why":"It offered a time outside the clinic's hours.","conversations":[{"id":"8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f","customer":"Lucía","state":"failed","passed":false,"score":58,"why":"It offered a time outside the clinic's hours."}]}],"fixes":[{"key":"q-3f9a1c7e","rank":1,"title":"Offer only open times","why":"Two conversations were offered a time the clinic is closed.","replaces":null,"add":"Offer only times the availability check returns.","scenarios":1,"seen_in":[{"test_run_id":"6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d","conversation_id":"8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f","scenario":"Books a cleaning","customer":"Lucía"}],"dismissed":false}]}],"could_not_start":[],"tries_fix":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No Run all with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-run-alls/{id}/try-fix":{"post":{"operationId":"tryTestFix","summary":"Try a fix on the draft","description":"Try it on a draft: the fix's change (or `text` instead) goes into the agent's draft instructions (a new draft when it has none), and a Run all of the scenarios it was seen in, plus up to two that passed, starts on that draft with the same customers per scenario. Nothing changes on the live version. It changes the agent's draft, so the key needs `agents:write` as well.\n\nRequires the `testing:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs Automated testing's own rules as the member who created the key: the key needs the scope and that member a role that writes tests (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nWhen the key asks for approval on changes, it waits for an owner's or admin's approval.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#tryTestFix"},"parameters":[{"name":"id","in":"path","required":true,"description":"The Run all id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fix_key":{"description":"The fix, as `GET /v1/test-run-alls/:id` names it (`fixes[].key`).","type":"string"},"version":{"description":"The version whose fix it is, in a Run all that compared two (default `A`).","type":"string","enum":["A","B"]},"text":{"description":"What to put in the instructions instead of the judge's wording.","type":"string","maxLength":2000}},"required":["fix_key"]},"example":{"fix_key":"q-3f9a1c7e"}}}},"responses":{"201":{"description":"The draft changed and its Run all started.","content":{"application/json":{"example":{"success":true,"data":{"run_all":{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","scenarios":6,"conversations":18,"started":[{"scenario_id":"4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b","test_run_id":"6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d"}],"skipped":[],"could_not_start":[]},"draft":{"agent_version_id":"c5c0810c-4614-4f0d-927d-62e587dad45e","label":"v13","created":false,"change":"added"}}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"tryTestFix"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key lacks `agents:write`, or the member who created the key can't write tests.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No Run all, or its version with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The agent is archived, so it can't be tested (`details.reason` is `AGENT_ARCHIVED`). `conflict`: The fix changed since it was read (`fix_changed`), the scenarios it was seen in were deleted, the agent has no live version, a test of the draft is still running (`draft_under_test`), the draft changed meanwhile (`version_changed`), or its Run all couldn't start (`run_not_started`: the change is saved in the draft).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: The workspace started too many test runs through the API recently.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-run-alls/{id}/dismissals":{"post":{"operationId":"dismissTestFix","summary":"Hide a fix","description":"Hides a fix in the Run all's What to fix, for everyone (Dismiss). `DELETE` brings it back.\n\nRequires the `testing:write` scope.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#dismissTestFix"},"parameters":[{"name":"id","in":"path","required":true,"description":"The Run all id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fix_key":{"description":"The fix, as `GET /v1/test-run-alls/:id` names it.","type":"string"}},"required":["fix_key"]},"example":{"fix_key":"q-3f9a1c7e"}}}},"responses":{"200":{"description":"Hidden.","content":{"application/json":{"example":{"success":true,"data":{"run_all_id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","fix_key":"q-3f9a1c7e","dismissed":true}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No Run all with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"restoreTestFix","summary":"Bring a hidden fix back","description":"Shows a fix the team hid in the Run all's What to fix again.\n\nRequires the `testing:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#restoreTestFix"},"parameters":[{"name":"id","in":"path","required":true,"description":"The Run all id.","schema":{"type":"string","format":"uuid"}},{"name":"fix_key","in":"query","required":false,"description":"The fix (or send it in the body).","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"fix_key":{"description":"The fix (or send it in the query string).","type":"string"}}},"example":{"fix_key":"q-3f9a1c7e"}}}},"responses":{"200":{"description":"Shown again.","content":{"application/json":{"example":{"success":true,"data":{"run_all_id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","fix_key":"q-3f9a1c7e","dismissed":false}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"restoreTestFix"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No Run all with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-runs/{id}/conversations/{conversation_id}/verdict-reviews":{"post":{"operationId":"reviewTestVerdict","summary":"Mark a verdict the judge got wrong","description":"Marks one of a test conversation's verdicts as wrong, with a note: a criterion it was graded on, by its id in the conversation's `criteria` (`C1`, `G2`, `K3`). Later runs hand the mark to the judge. Marking the same criterion again replaces the note; `DELETE` takes the mark back.\n\nRequires the `testing:write` scope.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#reviewTestVerdict"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test run id.","schema":{"type":"string","format":"uuid"}},{"name":"conversation_id","in":"path","required":true,"description":"The conversation's id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"criterion_key":{"description":"The criterion's id (`C1`, `G2`, `K3`).","type":"string"},"note":{"description":"Why the verdict is wrong.","type":"string","maxLength":500}},"required":["criterion_key"]},"example":{"criterion_key":"G2","note":"The customer did confirm the time."}}}},"responses":{"200":{"description":"Marked.","content":{"application/json":{"example":{"success":true,"data":{"conversation_id":"8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f","criterion_key":"G2","note":"The customer did confirm the time.","reviewed_at":"2026-10-27T18:40:11.204+00:00"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The criterion isn't one the conversation was graded on.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No test run or conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"removeTestVerdictReview","summary":"Take a verdict mark back","description":"Takes back the mark on one of a test conversation's verdicts.\n\nRequires the `testing:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#removeTestVerdictReview"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test run id.","schema":{"type":"string","format":"uuid"}},{"name":"conversation_id","in":"path","required":true,"description":"The conversation's id.","schema":{"type":"string","format":"uuid"}},{"name":"criterion_key","in":"query","required":false,"description":"The criterion's id (or send it in the body).","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"criterion_key":{"description":"The criterion's id (or send it in the query string).","type":"string"}}},"example":{"criterion_key":"G2"}}}},"responses":{"200":{"description":"Taken back.","content":{"application/json":{"example":{"success":true,"data":{"conversation_id":"8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f","criterion_key":"G2","removed":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"removeTestVerdictReview"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't do this or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No test run or conversation with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/release-summary":{"get":{"operationId":"getAgentReleaseSummary","summary":"Get a version's release summary","description":"What Automated testing says of an agent version before it goes live: the channels that will answer with it, the test chats tried on it (and which reply was picked or preferred), its last simulated test with the version it was compared with (`worse` counts the customers it failed that the other version passed), and its calls. Owners, admins and builders read it.\n\nRequires the `testing:read` scope.\n\nIt runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Automated testing"],"x-required-scope":"testing:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#getAgentReleaseSummary"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"version_id","in":"query","required":true,"description":"The version (`GET /v1/agents/:id` lists them).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The summary.","content":{"application/json":{"example":{"success":true,"data":{"agent_version_id":"2ea87934-6e03-4db7-80f2-ac753b724c82","channels":["Clínica Sol WhatsApp"],"chats":{"count":3,"messages":18,"picked":1,"preferred":1},"simulated":{"test_run_id":"6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d","scenario":"Books a cleaning","passed":5,"total":6,"compared":{"label":"v12","passed":4,"total":6},"worse":1},"calls":{"count":2,"last_seconds":94}}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner, admin or builder, has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent or version with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/goals/progress":{"get":{"operationId":"getAgentGoalProgress","summary":"Get an agent's goal progress","description":"The agent's goals and how each is doing against its targets in the current period (or as of a day, or over a date range): met conversations, evaluated ones, the rate, the share of the target reached and what remains, and each goal's trend. Rates, `percent_of_target` and `trend_value` are percentages (0 to 100); `remaining_to_target` is a sentence. Days are UTC days, and `to` and `as_of` count through the end of theirs.\n\nRequires the `goals:read` scope.\n\nIt runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Goals"],"x-required-scope":"goals:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/goals#getAgentGoalProgress"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"channel_id","in":"query","required":false,"description":"Only the goals of one of the agent's channels.","schema":{"type":"string","format":"uuid"}},{"name":"as_of","in":"query","required":false,"description":"The day the periods are counted at, through its end (default: now) (YYYY-MM-DD).","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"With `to`: a date range instead of the targets' periods (YYYY-MM-DD).","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"With `from`: the range's last day, counted through its end (YYYY-MM-DD).","schema":{"type":"string"}}],"responses":{"200":{"description":"The goals with their targets' progress.","content":{"application/json":{"example":{"success":true,"data":{"goals":[{"id":"7b7b8def-a340-4bfd-b164-779fa5843735","title":"Books an appointment","description":"","active":true,"channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","channel_name":"Clínica Sol WhatsApp","targets":[{"id":"5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f","period":"month","target_type":"rate","target_count":null,"target_rate":40,"is_primary":true,"progress":{"period_start":"2026-10-01T00:00:00+00:00","period_end":"2026-11-01T00:00:00+00:00","met_count":31,"total_evaluations":88,"current_rate":35.2,"percent_of_target":88,"remaining_to_target":"4.8% more needed"}}],"stats":{"total_evaluations":88,"met_count":31,"success_rate":35,"trend":"up","trend_value":6}}],"as_of":"2026-10-28T23:59:59.999Z"}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `validation_error`: `channel_id` isn't one of the agent's channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't a member who reads the agent, has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/goals/analytics":{"get":{"operationId":"getAgentGoalAnalytics","summary":"Get an agent's goal analytics","description":"How the agent's goals did over time, by day, week, month or quarter: the totals, a series per goal, and each active goal's rate and recent trend. With `compare_version_ids`, two versions of the agent side by side instead: each goal's rate on each and the winner. Rates are percentages (0 to 100). Days are UTC days, and `to` counts through the end of its day.\n\nRequires the `goals:read` scope.\n\nIt runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Goals"],"x-required-scope":"goals:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/goals#getAgentGoalAnalytics"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"period","in":"query","required":false,"description":"The series' step.","schema":{"type":"string","enum":["day","week","month","quarter"],"default":"month"}},{"name":"channel_id","in":"query","required":false,"description":"Only one channel.","schema":{"type":"string","format":"uuid"}},{"name":"version_id","in":"query","required":false,"description":"Only one version.","schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"description":"The window's first day (default: by period) (YYYY-MM-DD).","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"The window's last day, counted through its end (default: now) (YYYY-MM-DD).","schema":{"type":"string"}},{"name":"compare_version_ids","in":"query","required":false,"description":"Two different versions, comma-separated: compares them instead.","schema":{"type":"string"}}],"responses":{"200":{"description":"The analytics, or the comparison.","content":{"application/json":{"example":{"success":true,"data":{"period":"week","date_range":{"from":"2026-10-01T00:00:00.000Z","to":"2026-10-28T23:59:59.999Z"},"totals":{"total_evaluations":88,"met_count":31,"success_rate":35},"time_series":[],"goals":[]}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't a member who reads the agent, has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-numbers":{"get":{"operationId":"listTestNumbers","summary":"List the test call numbers","description":"The workspace's test numbers, where free test calls go (up to four), each `verified` or waiting for its code (`pending`), and today's free test calls (`quota`). Owners, admins, builders and operators read them.\n\nRequires the `testing:read` scope.\n\nIt runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Automated testing"],"x-required-scope":"testing:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#listTestNumbers"},"responses":{"200":{"description":"The numbers and the day's quota.","content":{"application/json":{"example":{"success":true,"data":{"available":true,"numbers":[{"id":"4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d","phone_number":"+528155550199","country":"MX","label":"Ana's mobile","status":"verified"}],"quota":{"enabled":true,"limit":10,"used":2,"remaining":8,"resets_at":"2026-10-29T06:00:00.000Z","own_line_unlimited":false,"unlimited":false},"can_manage":true,"verification_available":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner, admin, builder or operator, has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"addTestNumber","summary":"Add a test call number","description":"Adds a number for free test calls (a mobile or landline in Mexico, the US or Canada) and texts it a code: it takes test calls once the code is sent to `POST /v1/test-numbers/:id/verification`. Owners and admins add them.\n\nRequires the `testing:write` scope.\n\nIt runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.\n\nEach code is a text message NuTalk pays for.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#addTestNumber"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone_number":{"description":"The number in E.164, or its 10 digits with `country`.","type":"string"},"country":{"description":"Its country.","type":"string","enum":["MX","US","CA"]},"label":{"description":"A name for it.","type":"string","maxLength":60}},"required":["phone_number"]},"example":{"phone_number":"+528155550199","label":"Ana's mobile"}}}},"responses":{"201":{"description":"Added; its code was texted.","content":{"application/json":{"example":{"success":true,"data":{"id":"4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d","phone_number":"+528155550199","country":"MX","label":"Ana's mobile","status":"pending"}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: Not a number in Mexico, the US or Canada, or a toll-free or premium one (`details.reason` `TEST_NUMBER_INVALID`, `TEST_NUMBER_COUNTRY_NOT_SUPPORTED`, `TEST_NUMBER_NOT_CALLABLE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner or admin, has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The workspace has four already (`TEST_NUMBER_LIMIT`), or this one (`TEST_NUMBER_DUPLICATE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: Too many codes texted lately (`TEST_NUMBER_TOO_MANY_CODES`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: Codes can't be texted right now (`TEST_NUMBER_VERIFICATION_UNAVAILABLE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/test-numbers/{id}":{"delete":{"operationId":"removeTestNumber","summary":"Remove a test call number","description":"Removes a test number: free test calls no longer go to it. Owners and admins remove them.\n\nRequires the `testing:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nIt runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#removeTestNumber"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test number id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Removed.","content":{"application/json":{"example":{"success":true,"data":{"id":"4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"removeTestNumber"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner or admin, has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No test number with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/test-numbers/{id}/verification":{"post":{"operationId":"verifyTestNumber","summary":"Verify a test call number, or text a new code","description":"With `code`, verifies the test number with the code it was texted; without one, texts it a new code. Owners and admins verify them.\n\nRequires the `testing:write` scope.\n\nIt runs the dashboard's own rules as the member who created the key. A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Automated testing"],"x-required-scope":"testing:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/testing#verifyTestNumber"},"parameters":[{"name":"id","in":"path","required":true,"description":"The test number id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"description":"The code texted to the number (4 to 10 digits).","type":"string"}}},"example":{"code":"482913"}}}},"responses":{"200":{"description":"Verified (`code_sent` false), or a new code sent (`code_sent` true).","content":{"application/json":{"example":{"success":true,"data":{"id":"4b5c6d7e-8f90-4a1b-9c2d-3e4f5a6b7c8d","phone_number":"+528155550199","country":"MX","label":"Ana's mobile","status":"verified","code_sent":false}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key isn't an owner or admin, has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No test number with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: A new code was asked for a number already verified (`TEST_NUMBER_ALREADY_VERIFIED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error`: The code is wrong or has expired (`TEST_NUMBER_CODE_INVALID`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: Too many tries or codes (`TEST_NUMBER_TOO_MANY_TRIES`, `TEST_NUMBER_TOO_MANY_CODES`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"`service_unavailable`: Codes can't be texted or checked right now (`TEST_NUMBER_VERIFICATION_UNAVAILABLE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/agents/{id}/knowledge":{"get":{"operationId":"listKnowledgeSources","summary":"List an agent's knowledge","description":"Lists the documents, pasted texts and web pages the agent answers from, newest first, with each one's status and size in chunks.\n\nRequires the `knowledge:read` scope.\n\n`status`: `pending` until the source is read, `processing` while it is chunked and embedded, `ready` when the agent answers from it, `error` when it could not be read (`error` says why). A source read again keeps answering from its previous version until the new one is ready.","tags":["Knowledge"],"x-required-scope":"knowledge:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/knowledge#listKnowledgeSources"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"kind","in":"query","required":false,"description":"Only sources of this kind.","schema":{"type":"string","enum":["file","text","url"]}},{"name":"status","in":"query","required":false,"description":"Only sources in this status.","schema":{"type":"string","enum":["pending","processing","ready","error"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of knowledge sources.","content":{"application/json":{"example":{"success":true,"data":[{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","agent_id":"550e8400-e29b-41d4-a716-446655440000","kind":"url","title":"Prices and services","status":"ready","chunk_count":42,"error":null,"url":"https://clinic.example.com/prices","crawl":true,"max_pages":25,"warnings":[],"ingested_at":"2026-10-12T18:10:42.318Z","created_at":"2026-10-12T18:10:31.904211+00:00","updated_at":"2026-10-12T18:10:42.318+00:00"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"addKnowledgeSource","summary":"Add a knowledge source","description":"Adds a source the agent answers from: pasted text (`kind: text`) or a public web page (`kind: url`, optionally crawling the same site's linked pages) as JSON, or a file as `multipart/form-data` with the file in the `file` field (PDF, Word .docx, HTML, CSV, TXT or Markdown; the whole request at most 4 MB, larger files are added in the dashboard). The source is stored at once, then read, chunked and embedded in the background: follow its `status` with GET.\n\nRequires the `knowledge:write` scope.\n\nFile uploads go in one request, so the request's size limit applies (about 4 MB through the API); larger documents are uploaded in the dashboard.\n\nThe change runs the dashboard's knowledge rules as the member who created the key: the key needs the scope and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Knowledge"],"x-required-scope":"knowledge:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/knowledge#addKnowledgeSource"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"description":"`text` or `url` (a file is sent as multipart instead).","type":"string","enum":["text","url"]},"title":{"description":"The source's title. Required for `text`; for `url` the site's name by default.","type":"string","minLength":1,"maxLength":200},"text":{"description":"`text` only: the text the agent answers from.","type":"string","minLength":1,"maxLength":1000000},"url":{"description":"`url` only: a public http(s) address.","type":"string","format":"uri","maxLength":2048},"crawl":{"description":"`url` only: also read the same site's linked pages.","type":"boolean","default":false},"max_pages":{"description":"`url` with `crawl` only: the most pages to read.","type":"integer","default":25,"minimum":1,"maximum":50}},"required":["kind"]},"example":{"kind":"url","url":"https://clinic.example.com/prices","title":"Prices and services","crawl":true,"max_pages":25}}}},"responses":{"201":{"description":"The source, `pending`. `ingest_started` is false when reading it could not be started now: it is retried within 30 minutes.","content":{"application/json":{"example":{"success":true,"data":{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","agent_id":"550e8400-e29b-41d4-a716-446655440000","kind":"url","title":"Prices and services","status":"pending","chunk_count":0,"error":null,"url":"https://clinic.example.com/prices","crawl":true,"max_pages":25,"warnings":[],"ingested_at":null,"created_at":"2026-10-12T18:10:31.904211+00:00","updated_at":"2026-10-12T18:10:31.904211+00:00","ingest_started":true}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The URL is not http(s) or points to a private network, or the file is missing, empty, too large or of another type (`details.reason`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/knowledge/{source_id}":{"get":{"operationId":"getKnowledgeSource","summary":"Get a knowledge source","description":"Returns one knowledge source of the agent, with its status, its error and its warnings.\n\nRequires the `knowledge:read` scope.\n\n`status`: `pending` until the source is read, `processing` while it is chunked and embedded, `ready` when the agent answers from it, `error` when it could not be read (`error` says why). A source read again keeps answering from its previous version until the new one is ready.","tags":["Knowledge"],"x-required-scope":"knowledge:read","security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/knowledge#getKnowledgeSource"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"source_id","in":"path","required":true,"description":"The knowledge source id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The knowledge source.","content":{"application/json":{"example":{"success":true,"data":{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","agent_id":"550e8400-e29b-41d4-a716-446655440000","kind":"url","title":"Prices and services","status":"ready","chunk_count":42,"error":null,"url":"https://clinic.example.com/prices","crawl":true,"max_pages":25,"warnings":[],"ingested_at":"2026-10-12T18:10:42.318Z","created_at":"2026-10-12T18:10:31.904211+00:00","updated_at":"2026-10-12T18:10:42.318+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same). `not_found`: No knowledge source with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"deleteKnowledgeSource","summary":"Remove a knowledge source","description":"Removes the source, its stored file or text and its chunks: the agent stops answering from it at once.\n\nRequires the `knowledge:write` scope.\n\nWhen the key's deletes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's knowledge rules as the member who created the key: the key needs the scope and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Knowledge"],"x-required-scope":"knowledge:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/knowledge#deleteKnowledgeSource"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"source_id","in":"path","required":true,"description":"The knowledge source id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The source was removed.","content":{"application/json":{"example":{"success":true,"data":{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","deleted":true}}}}},"202":{"description":"Held for approval: this key's deletes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteKnowledgeSource"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same). `not_found`: No knowledge source with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateKnowledgeSource","summary":"Edit a text source","description":"Gives a text source a new title, new text, or both, and reads it again: the agent keeps answering from the old text until the new one is ready. A file is replaced by adding the new one and removing the old one; a web page is read again with reingest.\n\nRequires the `knowledge:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's knowledge rules as the member who created the key: the key needs the scope and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.\n\nWhen the key's changes wait for approval, a body over 64 KB can't be held: add the text as a new source and remove the old one instead.","tags":["Knowledge"],"x-required-scope":"knowledge:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/knowledge#updateKnowledgeSource"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"source_id","in":"path","required":true,"description":"The knowledge source id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"description":"The new title.","type":"string","minLength":1,"maxLength":200},"text":{"description":"The new text.","type":"string","minLength":1}}},"example":{"text":"Consultations cost $800 MXN and last 30 minutes."}}}},"responses":{"200":{"description":"The source, back to `pending`.","content":{"application/json":{"example":{"success":true,"data":{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","agent_id":"550e8400-e29b-41d4-a716-446655440000","kind":"text","title":"Prices and services","status":"pending","chunk_count":42,"error":null,"url":null,"crawl":false,"max_pages":null,"warnings":[],"ingested_at":"2026-10-12T18:10:42.318Z","created_at":"2026-10-12T18:10:31.904211+00:00","updated_at":"2026-10-12T18:10:42.318+00:00"}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"updateKnowledgeSource"}}}}}},"400":{"description":"`validation_error`: The body is not JSON, or a field is missing or invalid (`details.errors` lists each field). `validation_error`: The source isn't a text source (`details.reason` `not_text_source`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same). `not_found`: No knowledge source with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict`: The source is being read right now (`details.reason` `still_reading`): try again when its status is `ready` or `error`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: The workspace started too many test runs, re-scorings or knowledge reads through the API (30 an hour, 5 a minute).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/knowledge/{source_id}/reingest":{"post":{"operationId":"reingestKnowledgeSource","summary":"Read a knowledge source again","description":"Reads the source again in the background: a web page is fetched anew, a file or text re-read with the agent's current document search settings. The agent keeps answering from the version it has until the new one is ready.\n\nRequires the `knowledge:write` scope.\n\nWhen the key's changes wait for approval, this call answers 202 with a change request instead, and runs once an owner or admin approves it.\n\nThe change runs the dashboard's knowledge rules as the member who created the key: the key needs the scope and that member a role that edits agents (owner, admin or builder). A key whose creator has left the workspace, and a reseller key, are refused.","tags":["Knowledge"],"x-required-scope":"knowledge:write","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/knowledge#reingestKnowledgeSource"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"source_id","in":"path","required":true,"description":"The knowledge source id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The source, back to `pending`.","content":{"application/json":{"example":{"success":true,"data":{"id":"7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a","agent_id":"550e8400-e29b-41d4-a716-446655440000","kind":"url","title":"Prices and services","status":"pending","chunk_count":42,"error":null,"url":"https://clinic.example.com/prices","crawl":true,"max_pages":25,"warnings":[],"ingested_at":"2026-10-12T18:10:42.318Z","created_at":"2026-10-12T18:10:31.904211+00:00","updated_at":"2026-10-12T18:10:42.318+00:00","ingest_started":true}}}}},"202":{"description":"Held for approval: this key's changes wait for an owner's or admin's approval, so nothing ran yet. `data.change_request` is the request (status `pending`); it runs once approved: follow it with GET /v1/change-requests/{id}.","content":{"application/json":{"example":{"success":true,"data":{"change_request":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"change","operation":"reingestKnowledgeSource"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same). `not_found`: No knowledge source with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agents/{id}/knowledge/search":{"get":{"operationId":"searchKnowledge","summary":"Search an agent's knowledge","description":"The passages the agent's knowledge search finds for a question, with the agent's own search settings, as \"Try a question\" does in its Knowledge tab. It changes nothing. `mode` is `hybrid`, or `keywords` when the semantic search was unavailable; with no passage, `reason` says why (`no_documents`, `nothing_relevant`, `search_failed`).\n\nRequires the `knowledge:read` scope.\n\nRead as the member who created the key, with a role that sees the agent.","tags":["Knowledge"],"x-required-scope":"knowledge:read","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/knowledge#searchKnowledge"},"parameters":[{"name":"id","in":"path","required":true,"description":"The agent id.","schema":{"type":"string","format":"uuid"}},{"name":"question","in":"query","required":true,"description":"The question, as a customer would ask it.","schema":{"type":"string","minLength":1,"maxLength":500}}],"responses":{"200":{"description":"The passages, best first.","content":{"application/json":{"example":{"success":true,"data":{"agent_id":"550e8400-e29b-41d4-a716-446655440000","question":"How much is a consultation?","mode":"hybrid","passages":[{"title":"Prices and services","section":"Consultations","excerpt":"A first consultation costs $800 MXN and lasts 30 minutes."}],"reason":null}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The member who created the key has a role that can't edit agents or has left the workspace, or the key is a reseller key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: No agent with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited`: The member who created the key searched too many times in a short time (the dashboard's own limit).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/change-requests":{"get":{"operationId":"listChangeRequests","summary":"List change requests","description":"The deletes and changes this key asked for that waited for an owner's or admin's approval, newest first, with what each one became. Only the key's own requests.\n\nAny valid key, including a verify-only key.","tags":["Approvals"],"x-required-scope":null,"security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/approvals#listChangeRequests"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only requests in this status (`expired` includes pending requests past their expiry).","schema":{"type":"string","enum":["pending","executing","executed","failed","rejected","expired"]}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"A page of change requests.","content":{"application/json":{"example":{"success":true,"data":[{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"pending","approval":"delete","operation":"deleteGoal","method":"DELETE","path":"/v1/goals/7b7b8def-a340-4bfd-b164-779fa5843735","summary":"Delete a goal: “Book qualified meeting” (DELETE /v1/goals/7b7b8def-a340-4bfd-b164-779fa5843735)","created_at":"2026-10-13T01:30:00.000Z","expires_at":"2026-10-14T01:30:00.000Z","decided_at":null,"finished_at":null,"result":null}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/change-requests/{id}":{"get":{"operationId":"getChangeRequest","summary":"Get a change request","description":"One delete or change this key asked for that waited for approval: `pending` (waiting for an owner or admin), `executing`, `executed` (approved and made: `result` holds what the endpoint answered), `failed` (approved, but the endpoint refused it: `result` says why), `rejected` or `expired` (nobody decided within 24 hours). Only the key's own requests.\n\nAny valid key, including a verify-only key.","tags":["Approvals"],"x-required-scope":null,"security":[{"apiKey":[]},{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/approvals#getChangeRequest"},"parameters":[{"name":"id","in":"path","required":true,"description":"The change request id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"The change request.","content":{"application/json":{"example":{"success":true,"data":{"id":"c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c","status":"executed","approval":"delete","operation":"deleteGoal","method":"DELETE","path":"/v1/goals/7b7b8def-a340-4bfd-b164-779fa5843735","summary":"Delete a goal: “Book qualified meeting” (DELETE /v1/goals/7b7b8def-a340-4bfd-b164-779fa5843735)","created_at":"2026-10-13T01:30:00.000Z","expires_at":"2026-10-14T01:30:00.000Z","decided_at":"2026-10-13T01:42:10.000Z","finished_at":"2026-10-13T01:42:11.000Z","result":{"status":200,"body":{"success":true,"data":{"id":"7b7b8def-a340-4bfd-b164-779fa5843735","deleted":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"`not_found`: No change request asked for with this key with this id in your workspace (ids of other workspaces answer the same).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reseller/me":{"get":{"operationId":"getResellerKey","summary":"Verify a reseller key","description":"Returns the reseller key's reseller, its scopes, whether it reaches every client or a list, and how many clients it reaches right now.\n\nTakes any valid reseller API key.","tags":["Resellers"],"x-required-scope":null,"security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#getResellerKey"},"responses":{"200":{"description":"The key and what it reaches.","content":{"application/json":{"example":{"success":true,"data":{"authenticated":true,"api_key":{"id":"7b7a0ce4-0000-4000-8000-000000000001","name":"Agency dashboard","prefix":"rk_3f9a1c2e","scopes":["clients:read","billing:read","agents:read","calls:read"],"client_access":"all","expires_at":null},"reseller":{"id":"0b1c2d3e-4f50-4617-8293-a4b5c6d7e8f9","name":"Agencia Delta","slug":"agencia-delta"},"clients_reachable":12,"verified_at":"2026-10-02T18:00:00.000Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`issuer_inactive`: The member who created the key is no longer an owner or admin of the reseller. `reseller_suspended`: The reseller workspace is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reseller/clients":{"get":{"operationId":"listResellerClients","summary":"List clients","description":"The reseller's client workspaces this key reaches, by name. Each row is the client itself, never data from inside its workspace.\n\nTakes a reseller API key with the `clients:read` scope.","tags":["Resellers"],"x-required-scope":"clients:read","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#listResellerClients"},"parameters":[{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only clients in this status.","schema":{"type":"string","enum":["trialing","active","suspended"]}},{"name":"search","in":"query","required":false,"description":"Part of the client's name or slug.","schema":{"type":"string","maxLength":100}}],"responses":{"200":{"description":"One page of clients.","content":{"application/json":{"example":{"success":true,"data":[{"id":"1c2d3e4f-5061-4728-93a4-b5c6d7e8f901","name":"Clínica Norte","slug":"clinica-norte","status":"active","created_at":"2026-09-01T16:00:00.000Z"}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"createResellerClient","summary":"Create a client","description":"Creates a client workspace under the reseller, as the dashboard's Add client does without a portal login (portal logins are made in the dashboard). Like the dashboard, only the reseller's owner creates clients: the key must have been created by the owner. A key limited to a list of clients does not reach the new one until it is added to its list.\n\nTakes a reseller API key with the `clients:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.","tags":["Resellers"],"x-required-scope":"clients:write","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#createResellerClient"},"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The client's name.","type":"string","minLength":2,"maxLength":100},"slug":{"description":"Lowercase letters, numbers and inner hyphens; unique across NuTalk.","type":"string","minLength":2,"maxLength":63},"contact_email":{"description":"The client's contact, who gets suspension notices.","type":"string","format":"email","maxLength":320}},"required":["name","slug","contact_email"]},"example":{"name":"Clínica Norte","slug":"clinica-norte","contact_email":"admin@clinicanorte.example"}}}},"responses":{"201":{"description":"The new client.","content":{"application/json":{"example":{"success":true,"data":{"id":"1c2d3e4f-5061-4728-93a4-b5c6d7e8f901","name":"Clínica Norte","slug":"clinica-norte","status":"active"}}}}},"400":{"description":"`validation_error`: A field is missing or invalid (`details.errors`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key lacks clients:write, or its creator is not the reseller's owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`slug_in_use`: Another workspace already uses this slug.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reseller/clients/{client_id}":{"patch":{"operationId":"updateResellerClient","summary":"Update a client","description":"Renames, re-slugs, suspends or reactivates one client: the dashboard's client Settings change, with the same rules. A suspended client keeps its data and stays readable; its workspace refuses changes made with a reseller key until it is reactivated, except pausing a campaign. Its campaigns and queued outbound calls stop where they are (calls in progress finish) and resume when it is reactivated.\n\nTakes a reseller API key with the `clients:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.","tags":["Resellers"],"x-required-scope":"clients:write","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#updateResellerClient"},"parameters":[{"name":"client_id","in":"path","required":true,"description":"The client's id (from GET /reseller/clients).","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"The client's name.","type":"string","minLength":2,"maxLength":100},"slug":{"description":"A new slug (same rule as on create).","type":"string","minLength":2,"maxLength":63},"status":{"description":"Suspend or reactivate.","type":"string","enum":["active","suspended"]},"notify_client":{"description":"With status suspended: email the client's contact once.","type":"boolean"},"reason":{"description":"The reason in that email.","type":"string","maxLength":500}}},"example":{"status":"suspended","notify_client":true,"reason":"Unpaid invoice DELTA-2026-0042"}}}},"responses":{"200":{"description":"The client as stored, and whether the suspension email went out (null when none was asked for).","content":{"application/json":{"example":{"success":true,"data":{"id":"1c2d3e4f-5061-4728-93a4-b5c6d7e8f901","name":"Clínica Norte","slug":"clinica-norte","status":"suspended","email_sent":true}}}}},"400":{"description":"`validation_error`: Nothing to update, or a field is invalid. `invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`slug_in_use`: Another workspace already uses this slug. `already_suspended`: A notified suspension raced another one: the client was suspended a moment ago. `suspended_by_nutalk`: NuTalk suspended this client: only NuTalk can reactivate it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"getResellerClient","summary":"Get a client","description":"One client: its workspace (name, slug, status, timezone) and the contact details the reseller keeps for it.\n\nTakes a reseller API key with the `clients:read` scope.","tags":["Resellers"],"x-required-scope":"clients:read","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#getResellerClient"},"parameters":[{"name":"client_id","in":"path","required":true,"description":"The client's id (from GET /reseller/clients).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The client.","content":{"application/json":{"example":{"success":true,"data":{"id":"1c2d3e4f-5061-4728-93a4-b5c6d7e8f901","name":"Clínica Norte","slug":"clinica-norte","status":"active","created_at":"2026-09-01T16:00:00.000Z","timezone":"America/Monterrey","contact":{"email":"admin@clinicanorte.example","phone":"+528112345678","website":"https://clinicanorte.example","billing_email":"facturas@clinicanorte.example","billing_reminder_days":5,"notes":null}}}}}},"400":{"description":"`invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reseller/usage":{"get":{"operationId":"getResellerUsage","summary":"Usage per client","description":"Usage per client in a window: calls that started and their seconds, and two credit sums at different prices, never added together. `client_charges` are the charges on the client's own ledger at your retail prices (calls and replies; what invoices bill). `pool_draws` are the rows on your pool tagged with the client, at platform cost (pool deductions and the platform cost of replies); a call's pool debit has no ledger row, so calls are missing from it. Each has a by-type breakdown. Debits only; refunds show in the client's transactions. One row per client, in the order of GET /reseller/clients; pass client_id for one client. `pool` is your own credit balance.\n\nTakes a reseller API key with the `billing:read` scope.\n\nCredits keep 2 decimals (1 credit = 1 MXN; a price can be 5.5 credits a minute or 0.75 a message). Every credit field keeps its whole-number type: a charge or a sum drops its fraction (16.5 reads 16), a balance is rounded down (-0.25 reads -1), so nothing reads larger than it is. Each has an `_exact` twin with the exact amount (a number with up to 2 decimals: 16.5 is 16.50 credits).","tags":["Resellers"],"x-required-scope":"billing:read","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#getResellerUsage"},"parameters":[{"name":"from","in":"query","required":false,"description":"Start of the window, inclusive (ISO 8601). Default: the start of the current UTC month.","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"End of the window, exclusive. Default: now. The window spans at most 92 days.","schema":{"type":"string","format":"date-time"}},{"name":"client_id","in":"query","required":false,"description":"Only this client.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}}],"responses":{"200":{"description":"One page of clients with their usage.","content":{"application/json":{"example":{"success":true,"data":[{"client":{"id":"1c2d3e4f-5061-4728-93a4-b5c6d7e8f901","name":"Clínica Norte","status":"active"},"calls":412,"call_seconds":51840,"client_charges":2455,"client_charges_exact":2455.5,"client_charges_by_type":{"usage_voice_answered":{"transactions":380,"credits":2160,"credits_exact":2160},"usage_whatsapp":{"transactions":185,"credits":295,"credits_exact":295.5}},"pool_draws":138,"pool_draws_exact":138.75,"pool_draws_by_type":{"usage_whatsapp":{"transactions":185,"credits":138,"credits_exact":138.75}}}],"period":{"from":"2026-09-01T00:00:00.000Z","to":"2026-10-01T00:00:00.000Z"},"pool":{"credits_available":48210,"credit_balance":48210,"credits_available_exact":48210.5,"credit_balance_exact":48210.5},"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: from or to is not an ISO 8601 date-time, from is not before to, or the window is longer than 92 days. `invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reseller/clients/{client_id}/invoices":{"get":{"operationId":"listResellerClientInvoices","summary":"List a client's invoices","description":"The invoices the reseller issued to one client, newest first.\n\nTakes a reseller API key with the `billing:read` scope.","tags":["Resellers"],"x-required-scope":"billing:read","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#listResellerClientInvoices"},"parameters":[{"name":"client_id","in":"path","required":true,"description":"The client's id (from GET /reseller/clients).","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}},{"name":"status","in":"query","required":false,"description":"Only invoices in this status.","schema":{"type":"string","enum":["draft","sent","paid","cancelled"]}}],"responses":{"200":{"description":"One page of invoices.","content":{"application/json":{"example":{"success":true,"data":[{"id":"3e4f5061-7283-494a-b5c6-d7e8f9011223","invoice_number":"DELTA-2026-0042","status":"sent","period_start":"2026-09-01T00:00:00.000Z","period_end":"2026-09-30T23:59:59.000Z","total_amount":4890,"currency":"MXN","created_at":"2026-10-01T15:00:00.000Z","sent_at":"2026-10-01T15:05:00.000Z","paid_at":null}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid (`details.errors` lists each one). `invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reseller/clients/{client_id}/invoices/{invoice_id}":{"get":{"operationId":"getResellerClientInvoice","summary":"Get a client invoice","description":"One invoice of one client, with its quantities, rates and costs per channel.\n\nTakes a reseller API key with the `billing:read` scope.","tags":["Resellers"],"x-required-scope":"billing:read","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#getResellerClientInvoice"},"parameters":[{"name":"client_id","in":"path","required":true,"description":"The client's id (from GET /reseller/clients).","schema":{"type":"string","format":"uuid"}},{"name":"invoice_id","in":"path","required":true,"description":"The invoice id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The invoice."},"400":{"description":"`invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same. `not_found`: No such invoice for this client.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reseller/clients/{client_id}/transactions":{"get":{"operationId":"listResellerClientTransactions","summary":"List a client's credit transactions","description":"One client's credit rows from both ledgers, newest first: the client's own (a call's or a reply's charge) and the reseller's rows tagged with the client (pool deductions, refunds, and the platform-cost twin of a reply, marked `platform_charge`). `ledger` says which ledger a row is in. Without from/to, every row.\n\nTakes a reseller API key with the `billing:read` scope.\n\nCredits keep 2 decimals (1 credit = 1 MXN; a price can be 5.5 credits a minute or 0.75 a message). Every credit field keeps its whole-number type: a charge or a sum drops its fraction (16.5 reads 16), a balance is rounded down (-0.25 reads -1), so nothing reads larger than it is. Each has an `_exact` twin with the exact amount (a number with up to 2 decimals: 16.5 is 16.50 credits).","tags":["Resellers"],"x-required-scope":"billing:read","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#listResellerClientTransactions"},"parameters":[{"name":"client_id","in":"path","required":true,"description":"The client's id (from GET /reseller/clients).","schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"description":"Start of the window, inclusive (ISO 8601). Default: the start of the current UTC month.","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"End of the window, exclusive. Default: now. The window spans at most 92 days.","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","required":false,"description":"Results per page.","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"offset","in":"query","required":false,"description":"Results to skip.","schema":{"type":"integer","default":0,"minimum":0}}],"responses":{"200":{"description":"One page of transactions.","content":{"application/json":{"example":{"success":true,"data":[{"id":"4f506172-8394-4a5b-c6d7-e8f901122334","type":"usage_voice_answered","amount":-16,"amount_exact":-16.5,"description":"Voice call (3 min)","reference_type":"call","reference_id":"1ba11348-e440-44de-a5cd-5fe62f4d9df6","created_at":"2026-10-02T17:42:10.000Z","ledger":"client","platform_charge":false}],"pagination":{"total":1,"limit":20,"offset":0,"has_more":false}}}}},"400":{"description":"`validation_error`: A query parameter is invalid, or the from/to window is longer than 92 days. `invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reseller/clients/{client_id}/access":{"get":{"operationId":"getResellerClientAccess","summary":"Get a client's team access","description":"The reseller's team members other than its owners (who reach every client), and whether each reaches this client. Only keys the reseller's owner created: in the dashboard only the owner sees and changes this.\n\nTakes a reseller API key with the `access:read` scope.","tags":["Resellers"],"x-required-scope":"access:read","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#getResellerClientAccess"},"parameters":[{"name":"client_id","in":"path","required":true,"description":"The client's id (from GET /reseller/clients).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The team, each member with their access to this client.","content":{"application/json":{"example":{"success":true,"data":{"client":{"id":"1c2d3e4f-5061-4728-93a4-b5c6d7e8f901","name":"Clínica Norte"},"members":[{"member_id":"5a617283-94a5-4b6c-97d8-e9f012233445","user_id":"6b728394-a5b6-4c7d-88e9-f01223344556","role":"admin","email":"ana@agencia.example","name":"Ana Treviño","has_access":true,"granted_at":"2026-09-15T15:00:00.000Z"},{"member_id":"7c8394a5-b6c7-4d8e-99f0-012233445566","user_id":"8d94a5b6-c7d8-4e9f-a001-122334455667","role":"builder","email":"luis@agencia.example","name":"Luis Garza","has_access":false,"granted_at":null}]}}}}},"400":{"description":"`invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key was not created by the reseller's owner (only the owner manages team access).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateResellerClientAccess","summary":"Change a client's team access","description":"Gives team members access to this client (`grant`) and takes it from others (`revoke`), as the dashboard's client Access tab does. Every id must be a member of the reseller other than an owner, or nothing changes. Granting a member who has access, or revoking one who has none, changes nothing. Only keys the reseller's owner created.\n\nTakes a reseller API key with the `access:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.","tags":["Resellers"],"x-required-scope":"access:write","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#updateResellerClientAccess"},"parameters":[{"name":"client_id","in":"path","required":true,"description":"The client's id (from GET /reseller/clients).","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"grant":{"description":"member_id values (from GET …/access) to give access to.","type":"array","maxItems":100,"items":{"description":"A member_id.","type":"string","format":"uuid"}},"revoke":{"description":"member_id values to take access from.","type":"array","maxItems":100,"items":{"description":"A member_id.","type":"string","format":"uuid"}}}},"example":{"grant":["7c8394a5-b6c7-4d8e-99f0-012233445566"]}}}},"responses":{"200":{"description":"With access:read, the team as GET answers it, after the change. A key without access:read gets back only the member ids it granted and revoked (client.id, granted, revoked).","content":{"application/json":{"example":{"success":true,"data":{"client":{"id":"1c2d3e4f-5061-4728-93a4-b5c6d7e8f901","name":"Clínica Norte"},"members":[{"member_id":"5a617283-94a5-4b6c-97d8-e9f012233445","user_id":"6b728394-a5b6-4c7d-88e9-f01223344556","role":"admin","email":"ana@agencia.example","name":"Ana Treviño","has_access":true,"granted_at":"2026-09-15T15:00:00.000Z"},{"member_id":"7c8394a5-b6c7-4d8e-99f0-012233445566","user_id":"8d94a5b6-c7d8-4e9f-a001-122334455667","role":"builder","email":"luis@agencia.example","name":"Luis Garza","has_access":true,"granted_at":"2026-10-02T18:00:00.000Z"}]}}}}},"400":{"description":"`validation_error`: Neither list names a member, an id is in both, or an id is not a UUID. `invalid_member_ids`: An id is not a member of the reseller's team, or is an owner. `invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key was not created by the reseller's owner (only the owner manages team access).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/reseller/clients/{client_id}/invoice-settings":{"get":{"operationId":"getResellerClientInvoiceSettings","summary":"Get a client's invoice settings","description":"Who the reseller bills for this client and on which terms. Until they are saved once, the values invoices use (`customized` false). An empty term (null) falls back to the reseller's default, in `reseller_defaults`.\n\nTakes a reseller API key with the `billing:read` scope.","tags":["Resellers"],"x-required-scope":"billing:read","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#getResellerClientInvoiceSettings"},"parameters":[{"name":"client_id","in":"path","required":true,"description":"The client's id (from GET /reseller/clients).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The client's invoice settings.","content":{"application/json":{"example":{"success":true,"data":{"client":{"id":"1c2d3e4f-5061-4728-93a4-b5c6d7e8f901","name":"Clínica Norte"},"customized":true,"settings":{"billing_company_name":"Clínica Norte S.A. de C.V.","billing_contact_name":"Laura Garza","billing_email":"facturas@clinicanorte.example","billing_address_line1":"Av. Constitución 100","billing_address_line2":null,"billing_city":"Monterrey","billing_state":"Nuevo León","billing_postal_code":"64000","billing_country":"Mexico","billing_tax_id":"CNO200101AB1","currency":null,"payment_terms":"net_30","tax_rate":null,"custom_notes":null},"reseller_defaults":{"currency":"MXN","payment_terms":"net_15","tax_rate":0.16},"updated_at":"2026-10-02T18:00:00.000Z"}}}}},"400":{"description":"`invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"updateResellerClientInvoiceSettings","summary":"Update a client's invoice settings","description":"Sets the fields sent, as the client's Settings → Invoices form saves them; the others keep their value. null clears a field, or returns currency, payment_terms or tax_rate to the reseller's default.\n\nTakes a reseller API key with the `billing:write` scope. Honours the `Idempotency-Key` header: retrying the same request with the same key within 24 hours replays the first successful answer.","tags":["Resellers"],"x-required-scope":"billing:write","security":[{"resellerKey":[]}],"externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/resellers#updateResellerClientInvoiceSettings"},"parameters":[{"name":"client_id","in":"path","required":true,"description":"The client's id (from GET /reseller/clients).","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"billing_company_name":{"description":"The company invoices are made out to (the client's name until set).","type":["string","null"],"maxLength":200},"billing_contact_name":{"description":"The person invoices are addressed to.","type":["string","null"],"maxLength":200},"billing_email":{"description":"Where invoices go.","type":["string","null"],"format":"email"},"billing_address_line1":{"description":"Address, first line.","type":["string","null"],"maxLength":200},"billing_address_line2":{"description":"Address, second line.","type":["string","null"],"maxLength":200},"billing_city":{"description":"City.","type":["string","null"],"maxLength":100},"billing_state":{"description":"State.","type":["string","null"],"maxLength":100},"billing_postal_code":{"description":"Postal code.","type":["string","null"],"maxLength":20},"billing_country":{"description":"Country (Mexico until set).","type":["string","null"],"maxLength":100},"billing_tax_id":{"description":"The client's tax id (RFC in Mexico).","type":["string","null"],"maxLength":50},"currency":{"description":"null: the reseller's default.","type":["string","null"],"enum":["MXN","USD","EUR",null]},"payment_terms":{"description":"null: the reseller's default.","type":["string","null"],"enum":["due_on_receipt","net_15","net_30",null]},"tax_rate":{"description":"A fraction: 0.16 is 16%. null: the reseller's default.","type":["number","null"],"minimum":0,"maximum":1},"custom_notes":{"description":"A note printed on this client's invoices.","type":["string","null"],"maxLength":2000}}},"example":{"billing_email":"facturas@clinicanorte.example","payment_terms":"net_30"}}}},"responses":{"200":{"description":"With billing:read, the client's invoice settings as GET answers them. A key without billing:read gets back only the fields it sent, as stored (client.id, settings, updated_at).","content":{"application/json":{"example":{"success":true,"data":{"client":{"id":"1c2d3e4f-5061-4728-93a4-b5c6d7e8f901","name":"Clínica Norte"},"customized":true,"settings":{"billing_company_name":"Clínica Norte S.A. de C.V.","billing_contact_name":"Laura Garza","billing_email":"facturas@clinicanorte.example","billing_address_line1":"Av. Constitución 100","billing_address_line2":null,"billing_city":"Monterrey","billing_state":"Nuevo León","billing_postal_code":"64000","billing_country":"Mexico","billing_tax_id":"CNO200101AB1","currency":null,"payment_terms":"net_30","tax_rate":null,"custom_notes":null},"reseller_defaults":{"currency":"MXN","payment_terms":"net_15","tax_rate":0.16},"updated_at":"2026-10-02T18:00:00.000Z"}}}}},"400":{"description":"`validation_error`: Nothing to update, an unknown field, or a value that is not allowed. `invalid_client_id`: The client id is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`client_not_found`: No client of this reseller with this id is reachable with this key: another reseller's client, a client off the key's list or outside its creator's grants, and ids that do not exist all answer the same.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"sk_<8 hex prefix>_<48 hex secret>","description":"A workspace API key from Settings → API keys."},"resellerKey":{"type":"http","scheme":"bearer","bearerFormat":"rk_<8 hex prefix>_<64 hex secret>","description":"A reseller API key from Settings → Reseller API keys (reseller workspaces only). On workspace endpoints it needs the NuTalk-Client-Id header."}},"parameters":{"ClientId":{"name":"NuTalk-Client-Id","in":"header","required":false,"description":"With a reseller API key: the client workspace this request reads (required; a client the key does not reach answers 404 `client_not_found`). With a workspace API key: leave it out (400 `client_context_not_supported`).","schema":{"type":"string","format":"uuid"}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"A unique value per operation (a UUID). Retrying the same request (same path and body) with the same key within 24 hours replays the first successful answer (`Idempotency-Replay: true`) instead of acting twice. Only successes are stored; an error runs again on retry. A request still running under the key answers 409 `IDEMPOTENCY_CONFLICT`.","schema":{"type":"string","maxLength":255}}},"schemas":{"Error":{"type":"object","required":["success","error"],"properties":{"success":{"const":false},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"unauthorized, invalid_api_key, forbidden, not_found, validation_error, rate_limited, internal_error (plus insufficient_credits / no_agent_deployed / do_not_contact on POST /calls, IDEMPOTENCY_CONFLICT, and IDEMPOTENCY_UNAVAILABLE on POST /conversations/{id}/messages)."},"message":{"type":"string"},"details":{"type":"object","additionalProperties":true}}}}},"Pagination":{"type":"object","properties":{"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"}}}},"responses":{"Unauthorized":{"description":"`unauthorized`: The Authorization header is missing or is not `Bearer sk_…`. `invalid_api_key`: The key is unknown, revoked or its secret is wrong. `api_key_expired`: The key is past the expiry date set in Settings → API keys.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"`forbidden`: The key lacks the operation's scope (`details.required_scope`, `details.granted_scopes`). `ip_not_allowed`: The request comes from an address that is not on the key's allowed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"`rate_limited`: The workspace used its requests for the hour or the minute (by default 1,000 an hour and 100 a minute; `Retry-After` says when to retry).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}},"InternalError":{"description":"`internal_error`: The request failed on our side; retrying is safe for reads.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}