API Reference
Approvals
Deletes and changes made with an API key can wait for an owner's or admin's approval before they run.
Each workspace API key has two settings in Settings → API Keys: Deletes wait for approval and Changes wait for approval. Both are on for a new key. While one is on, a call of that kind does not run when the key makes it: it becomes a change request, and an owner or admin of the workspace approves or rejects it under Approvals in the same tab. Each request shows where the change lands (the agent, the channel, or both), what changes there (an agent's instructions section by section, the live version, who answers a channel, or each field next to how the item looks now), what approving does next, and the exact call it runs. Approving runs the call once; rejecting it, or nobody deciding within 24 hours, runs nothing.
Anyone who may edit a key can turn its approvals on; only owners and admins can turn them off. They apply to the REST API and to the MCP server alike: every MCP tool runs its REST endpoint, so a held call is held whichever way it came. Following a request needs no scope: a key reads only the requests it made.
Which calls wait
| Setting | Calls that wait |
|---|---|
| Deletes | Every DELETE, cancelling a clinic appointment (POST /v1/clinic/appointments/:id/cancel) and cancelling a business appointment (PATCH /v1/business/appointments/:id with status: "cancelled"). |
| Changes | Every PATCH and PUT (edits), and the actions that change something that already exists: deploying an agent, starting a campaign, rescheduling a clinic appointment or setting its status, and resolving, reopening, archiving, unarchiving or resuming the AI of a conversation. |
| Never | Reading, creating (a contact, a message, an enrollment, an import…), and stopping something: pausing a campaign, pausing the AI of a conversation, ending a call, cancelling a contact import, and switching a channel's AI off or pausing it (PUT /v1/channels/:id/ai-mode with mode off or paused). Stopping is a safety action: an approval would only delay it. |
Each reference page marks these calls with Can wait for approval. A key without the scope a call needs gets the endpoint's own 403, held or not.
Every call that can wait (67)
Deletes
- DELETE /v1/agents/:id
- DELETE /v1/contacts/:id
- DELETE /v1/contact-lists/:id
- DELETE /v1/contact-lists/:id/members/:contact_id
- DELETE /v1/campaigns/:id
- DELETE /v1/goals/:id
- DELETE /v1/competencies/:id
- DELETE /v1/channels/:id/agent
- DELETE /v1/webhooks/:id
- DELETE /v1/clinic/locations/:id
- DELETE /v1/clinic/location-hours/:id
- DELETE /v1/clinic/rooms/:id
- DELETE /v1/clinic/spots/:id
- DELETE /v1/clinic/doctors/:id
- DELETE /v1/clinic/days-off/:id
- DELETE /v1/clinic/services/:id
- DELETE /v1/business/hosts/:id
- DELETE /v1/business/availability-windows/:id
- DELETE /v1/business/days-off/:id
- DELETE /v1/business/closures/:id
- DELETE /v1/business/meeting-types/:id
- DELETE /v1/business/calendar-connections/:id
- POST /v1/clinic/appointments/:id/cancel
- DELETE /v1/test-scenarios/:id
- DELETE /v1/agents/:id/knowledge/:source_id
Changes
- PATCH /v1/agents/:id
- POST /v1/agents/:id/deploy
- PATCH /v1/conversations/:id
- POST /v1/conversations/:id/resolve
- POST /v1/conversations/:id/reopen
- POST /v1/conversations/:id/archive
- POST /v1/conversations/:id/unarchive
- POST /v1/conversations/:id/resume-ai
- PATCH /v1/contacts/:id
- PATCH /v1/contact-fields/:id
- PATCH /v1/contact-lists/:id
- POST /v1/contact-imports/:id/start
- PATCH /v1/campaigns/:id
- POST /v1/campaigns/:id/start
- PATCH /v1/goals/:id
- PATCH /v1/competencies/:id
- PATCH /v1/channels/:id
- PUT /v1/channels/:id/agent
- PUT /v1/channels/:id/ai-mode
- PUT /v1/channels/:id/scheduling
- PATCH /v1/webhooks/:id
- PATCH /v1/clinic/locations/:id
- PATCH /v1/clinic/location-hours/:id
- PATCH /v1/clinic/rooms/:id
- PATCH /v1/clinic/spots/:id
- PATCH /v1/clinic/doctors/:id
- PUT /v1/clinic/doctors/:id/weekly-hours
- PATCH /v1/clinic/services/:id
- PATCH /v1/clinic/settings
- PATCH /v1/business/hosts/:id
- PATCH /v1/business/availability-windows/:id
- PATCH /v1/business/days-off/:id
- PATCH /v1/business/closures/:id
- PATCH /v1/business/meeting-types/:id
- PATCH /v1/business/calendar-connections/:id
- PATCH /v1/business/appointments/:id
- PATCH /v1/clinic/appointments/:id
- POST /v1/clinic/appointments/:id/reschedule
- POST /v1/clinic/appointments/:id/status
- PATCH /v1/test-scenarios/:id
- POST /v1/test-runs/:id/evaluate
- POST /v1/agents/:id/knowledge/:source_id/reingest
The answer to a held call
A held call answers 202 Accepted with the change request in data.change_request, status pending, and its URL in the Location header. Nothing has changed yet: tell your user the change waits for approval, never that it is done. Making the same call again while it waits (same method, path, query and body) answers the same request, so a retry never asks twice.
curl -X DELETE "https://www.nutalk.ai/v1/goals/7b7b8def-a340-4bfd-b164-779fa5843735" \ -H "Authorization: Bearer $NUTALK_API_KEY"{
"success": true,
"data": {
"change_request": {
"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
}
},
"message": "This change waits for an owner's or admin's approval in NuTalk. It runs once approved; check it with GET /v1/change-requests/{id}."
}Statuses
A request waits up to 24 hours (expires_at). Follow it with GET /v1/change-requests/:id, or list the key's requests with GET /v1/change-requests?status=pending.
| Status | Meaning |
|---|---|
| pending | Waiting for an owner or admin. Nothing has run. |
| executing | Approved: the call is running now. |
| executed | Approved and made. result holds the HTTP status and body the endpoint answered. |
| failed | Approved, but the endpoint refused the call or it could not run (for example, the key was revoked or expired in between). result says why. |
| rejected | An owner or admin rejected it. Nothing ran. |
| expired | Nobody decided within 24 hours. Nothing ran. |
What approving does
- It runs the call once, exactly as the key made it (the same endpoint, query, body and
Idempotency-Key), as the key. Every check the endpoint makes runs again then, with the key's scopes as they are at that moment. - A key revoked or expired since it asked runs nothing: the request ends
failed. - Only one decision ever lands, and a request is never run twice: one interrupted while it ran reads as
failed, because the change may have been made. Check the item before asking again.
Through MCP
MCP tools return the pending request too: a tool whose call is held returns data.change_request with status pending, and the server tells the assistant to say it waits for approval, never that it is done. A key that can delete or change something and has either approval on also gets two tools to follow its requests: list_change_requests and get_change_request.
Limits
How many requests can wait
A key can have up to 20 requests waiting at once, and a workspace up to 1,000. Past that, a call that would wait answers 429 with approval_queue_full until an owner or admin decides some.
List change requests
/v1/change-requestsThe 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.
none (any valid key)Query parameters
| Name | Type | Description |
|---|---|---|
limit | integer | Results per page. 1–100. Default: |
offset | integer | Results to skip. At least 0. Default: |
status | string | Only requests in this status ( |
Example request
curl -X GET "https://www.nutalk.ai/v1/change-requests?status=pending" \ -H "Authorization: Bearer YOUR_API_KEY"A page of change requests.
{
"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
}
}Errors
| Status | Code | When |
|---|---|---|
| 400 | validation_error | A query parameter is invalid (details.errors lists each one). |
Plus the errors every request can return (401, 403, 429, 500): Errors.
Get a change request
/v1/change-requests/:idOne 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.
none (any valid key)Path parameters
| Name | Type | Description |
|---|---|---|
idRequired | uuid | The change request id. |
Example request
curl -X GET "https://www.nutalk.ai/v1/change-requests/c4a7e2d1-5b3f-4e8a-9c6d-2f1b0a9e8d7c" \ -H "Authorization: Bearer YOUR_API_KEY"The change request.
{
"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
}
}
}
}
}Errors
| Status | Code | When |
|---|---|---|
| 404 | not_found | No change request asked for with this key with this id in your workspace (ids of other workspaces answer the same). |
Plus the errors every request can return (401, 403, 429, 500): Errors.