{"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`, `contacts:read`, `contacts:write`, `contact_lists:read`, `contact_lists:write`, `calls:read`, `calls:write`, `conversations:read`, `conversations:write`, `campaigns:read`, `campaigns:write`, `goals:read`, `goals:write`, `competencies:read`, `competencies:write`, `testing:read`, `testing:write`, `knowledge:read`, `knowledge:write`, `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: 1,000 requests per hour and 100 per minute. RateLimit-* and X-RateLimit-* headers report the window; 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.","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":"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; choose their agent, AI mode and scheduling.","externalDocs":{"url":"https://www.nutalk.ai/en/docs/api/channels"}},{"name":"Webhooks","description":"Register signed endpoints for call and conversation events.","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. Archived (deleted) agents are not listed. `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}},{"$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","updated_at":"2026-10-02T03:56:00.896+00:00","deployed_version":{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.1"}}],"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`, 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\nThe API sets the name, description, instructions, language, identity, purpose and call opening. Voice, tools (calendars, transfers, data connectors), knowledge and automations are configured in the dashboard.\n\nA new agent is not on any channel. Phone numbers, WhatsApp numbers and website widgets are connected to an agent in the dashboard; once connected, deploying a new version moves the 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","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","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","deployment_status":"draft","created_at":"2026-10-02T02:20:32.324764+00:00"},{"id":"2ea87934-6e03-4db7-80f2-ac753b724c82","version_label":"v1.1","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 (the latest label's minor number plus one, e.g. v1.0 → v1.1) that copies the rest of the latest version's configuration; the deployed version keeps answering until you deploy the new one.\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.","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"}}}}},"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"}}}},"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. Archived agents disappear from the list and cannot be fetched.\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. 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`.\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#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 (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","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"}}}},"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). Transcripts and summaries are on GET /v1/calls/{id}.\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#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":"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","is_test":false}],"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 (1 credit = 1 MXN; prices can have decimals, e.g. 5.5 credits a call minute, charged per started minute).\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","is_test":false,"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` and marks it `canceled` (`call_outcome: terminated_via_api`). Works for calls on NuTalk phone numbers (Telnyx) and on the website widget.\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"}}}}}}},"/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 the stored copies the inbox shows; 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/public/whatsapp-media/…/voice.ogg","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).","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`: `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"}}}},"/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"}}}},"/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. Members are added in the dashboard (CSV import, CRM sync, campaign imports).\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","minimum":1,"maximum":1073741824},"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 those rows 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 `phone_country`), `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 the default workflow: call the contact with the agent on the outbound channel, 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 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\nThe workflow (multi-step sequences, WhatsApp templates, CRM steps, retries per step) is edited in the dashboard; the API cannot set it.\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 retry settings, which are edited in the dashboard. The cap that limits calls to one contact is `max_calls_per_lead_per_day`.\n\nA campaign skips contacts already enrolled in it (deduplication `skip`). Enrollments to a draft or paused campaign answer 409 unless API import is enabled on the campaign (dashboard, Integrations tab): then contacts wait (`status: enrolled`) and start when the campaign does.","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}}}},"required":["name"]},"example":{"name":"Summer Sale Campaign","description":"Outreach for summer promotion","type":"outbound","outbound_channel_id":"9f19ef32-eaef-452c-bebc-14d9759cdb64","business_hours":{"timezone":"America/Los_Angeles","schedule":{"monday":{"start":"10:00","end":"18:00"},"tuesday":{"start":"10:00","end":"18:00"},"wednesday":{"start":"10:00","end":"18:00"}}}}}}},"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"}}}}},"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, 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, outbound channel, default contact list and inbound webhook URL.\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":null}}}}},"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. Edit the step in the dashboard workflow editor.\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 retry settings, which are edited in the dashboard. 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}}}}},"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, or invalid `business_hours`. `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}/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 in the dashboard (connect the agent to the number, deploy it, connect the CRM), 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","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}/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 file of any size (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"}}}},"/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.","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"]}},{"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","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}],"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"}}}},"/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\nChannels are created in the dashboard: buying or porting a number, connecting WhatsApp or Meta pages and installing the website widget need a person (carrier purchase, Meta login).","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","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,"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"}}},"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","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,"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}/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).","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"}}}},"/webhooks":{"get":{"operationId":"listWebhooks","summary":"List webhook endpoints","description":"Lists the workspace's webhook endpoints with their events and last delivery result. 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"],"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`.","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"]}},"description":{"description":"What the endpoint is for.","type":"string","maxLength":500}},"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"],"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.","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`, or a call event or `*` (the default) and the key lacks `calls:read`.","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"],"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`.","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"]}},"description":{"description":"New description, or null.","type":["string","null"],"maxLength":500},"is_active":{"description":"false: stop deliveries; true: resume.","type":"boolean"}}},"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"],"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.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"`forbidden`: The key lacks `conversations:read` (or `calls:read`) and either `events` names a conversation event (or a call event or `*`), or the request changes the URL or turns the endpoint back on while its current or new events name one.","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"}}}},"/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/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, 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"}]},"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"]},"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"}]},"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"}}}}}}},"/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","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","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","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`.\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#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}}},"example":{"title":"Sales lead","timezone":"America/Mexico_City"}}}},"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/Mexico_City","status":"active","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, or `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"}}}},"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"}}},"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","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"]}},{"$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/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, status, patient phone or contact.\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"}},{"$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. Without it, the dashboard's default: the first connected channel with clinic AI enabled (no reminders when there is none).","type":"string","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}/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":[],"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":{}}],"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","weight":1}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","weight":1}],"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":["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":["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":[],"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":{}}],"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","weight":1}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","weight":1}],"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":[],"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":{}}],"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","weight":1}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","weight":1}],"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":["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":["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":[],"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":{}}],"goals":[{"goal_id":"7b7b8def-a340-4bfd-b164-779fa5843735","weight":1}],"competencies":[{"competency_id":"b97659d1-b147-4831-86d0-3e36b6bd3fee","weight":1}],"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 scores each conversation on the goals and competencies you name (by default the scenario's). 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":"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,"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 goals and competencies scored, and every simulated conversation with its transcript, the evaluator's summary and its verdict on each goal and competency.\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.","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":"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,"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 goals and competencies as they are now, for example after rewording a goal. 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}/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"}}}},"/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"}}}},"/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.","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 1,000 requests per hour or 100 per 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"}}}}}}}