API Reference
MCP Server
Connect Claude, Cursor or your own AI agent to your workspace with the Model Context Protocol. The assistant can look up calls, contacts, campaigns and more using a workspace API key.
Endpoint
https://www.nutalk.ai/v1/mcpStreamable HTTP, stateless
Authentication
Your workspace API key as a Bearer token, the same key the REST API uses.
Reads by default
Read tools run GET endpoints of the REST API. Tools that change data appear only for a key with their Write scope and the matching switch turned on for the key: setting up the workspace, or acting on conversations and reaching customers. Both are off on a new key.
1. Create a key for the assistant
Go to Settings → API Keys, create a key named after the assistant (for example "Claude Desktop") and choose Read only access. The new-key screen shows the setup below with your key already filled in. To let the assistant set up the workspace (agents, contacts, lists, campaigns, clinic and business scheduling, goals, competencies, tests, knowledge and channels), give the key Write on what it should change and turn on Let MCP set up the workspace. To let it act on conversations, send messages, start campaigns, put an agent live, book appointments or register webhooks, an owner or admin also turns on Let MCP act on conversations and reach customers.
Give each assistant its own key
A key's Write scopes let the assistant change those resources through MCP, and the key also works on the REST API, where a coding assistant with a terminal could use it. A Read only key keeps the assistant read-only everywhere. Revoking the key in Settings cuts the assistant off on its next request.
2. Connect your client
- Claude Code: run the command in a terminal, then check the connection with
/mcp. It is added to the current project only; add--scope userto use it everywhere. For a team, commit a.mcp.jsonwith"Authorization": "Bearer ${NUTALK_API_KEY}"and let each person set the variable (see Build with AI agents). - Codex: Codex reads the key from the
NUTALK_API_KEYenvironment variable and the command adds the server to~/.codex/config.toml([mcp_servers.nutalk]withurlandbearer_token_env_var). - Claude Desktop: Settings → Developer → Edit Config, add the server to
claude_desktop_config.jsonand restart Claude. It runs throughmcp-remote(Node.js 18 or later), because Claude's own connectors sign in with OAuth only. - Cursor: add the server to
~/.cursor/mcp.json(every project) or.cursor/mcp.json(one project). - OpenAI API: attach the server to a request with the Responses API's
mcptool and the model you use there.
claude mcp add --transport http nutalk https://www.nutalk.ai/v1/mcp --header "Authorization: Bearer YOUR_API_KEY"ChatGPT app
ChatGPT's connectors (developer mode) sign in with OAuth or no authentication and cannot send an API key, so this server cannot be added in the ChatGPT app yet. Use the OpenAI API example above from your own code instead.
Any other MCP client works if it supports Streamable HTTP with a custom header: https://www.nutalk.ai/v1/mcp with Authorization: Bearer YOUR_API_KEY.
Tools
The server lists only the tools your key's scopes allow: a Read only key gets all 60 read tools, a key with contacts:read gets whoami plus the 6 contact tools, and a Verify only key gets just whoami. A key whose deletes or changes wait for approval also gets list_change_requests and get_change_request once it holds a Write scope. Each Write scope adds the tools that change its resource once the key lets MCP set up the workspace (87 setup tools in all); the 23 tools that act on conversations or reach customers need their Write scope and the key allowed to reach customers through MCP, and those that also set up the workspace (webhooks, campaign starts, putting an agent live, bookings) need both switches. Each tool runs the REST endpoint shown, with the same scope check, workspace boundary, audit record and response.
| Tool | What it returns | Scope | REST endpoint |
|---|---|---|---|
| whoami | Verify this API key and return its workspace (id, name, status) and the key's name, prefix and scopes. Needs no scope. | none | GET /v1/auth/verify |
| list_change_requests | List the deletes and changes this key asked for that wait (or waited) for an owner's or admin's approval, with what each one became; filter by status. | none | GET /v1/change-requests |
| get_change_request | Get one change request this key asked for: pending, executed (with what the endpoint answered), failed, rejected or expired. | none | GET /v1/change-requests/{id} |
| list_agents | List the workspace's AI agents (voice and messaging). | agents:read | GET /v1/agents |
| get_agent | Get one agent with its deployed version's instructions and its version history. | agents:read | GET /v1/agents/{id} |
| list_channels | List channels (voice numbers, WhatsApp, Instagram, Messenger, web widget, iMessage) with their phone number, status and health. | channels:read | GET /v1/channels |
| get_channel | Get one channel. | channels:read | GET /v1/channels/{id} |
| list_contacts | List contacts, newest first; search by name, phone or email, or filter by tag. | contacts:read | GET /v1/contacts |
| get_contact | Get one contact. | contacts:read | GET /v1/contacts/{id} |
| list_contact_lists | List reusable contact lists. | contact_lists:read | GET /v1/contact-lists |
| get_contact_list | Get one contact list, optionally with a page of its members. | contact_lists:read | GET /v1/contact-lists/{id} |
| list_calls | List calls, newest first, with status, direction, numbers, duration and outcome; filter by status, direction, number, channel or start time. | calls:read | GET /v1/calls |
| get_call | Get one call with its transcript, summary, outcome and where its recording is stored (the audio itself is GET /v1/calls/{id}/recording over REST). | calls:read | GET /v1/calls/{id} |
| get_call_job | Get the queue job of an outbound call that was queued instead of placed (the queue_job_id of POST /v1/calls): its status and, once completed, the call id. | calls:read | GET /v1/call-jobs/{id} |
| list_conversations | List WhatsApp, SMS, Instagram, Messenger, Apple Messages and website conversations, newest activity first, with the contact, assignee, AI handling and reply window; filter by channel, status, assignee, contact, awaiting reply or date, or search by name or phone. | conversations:read | GET /v1/conversations |
| get_conversation | Get one conversation with its contact, assignee, AI handling, summary and evaluation. | conversations:read | GET /v1/conversations/{id} |
| list_conversation_messages | List one conversation's messages (newest first, or oldest first with order=asc; with after, in the order NuTalk stored them, to follow it) with sender, text, attachments, voice-note transcriptions, templates and delivery status. | conversations:read | GET /v1/conversations/{conversation_id}/messages |
| assign_conversation | Assign a conversation to a teammate (assignee_id), or unassign it (null); the teammate is notified in their inbox. | conversations:write | PATCH /v1/conversations/{id} |
| resolve_conversation | Close a conversation, as Resolve does in the inbox. | conversations:write | POST /v1/conversations/{id}/resolve |
| reopen_conversation | Reopen a resolved conversation. | conversations:write | POST /v1/conversations/{id}/reopen |
| archive_conversation | Archive a conversation by hand; it stays archived when the customer writes again. | conversations:write | POST /v1/conversations/{id}/archive |
| unarchive_conversation | Bring an archived conversation back to the inbox, open. | conversations:write | POST /v1/conversations/{id}/unarchive |
| pause_conversation_ai | Switch the AI off on a conversation so a person answers (the inbox's AI-off switch, shared by the team). | conversations:write | POST /v1/conversations/{id}/pause-ai |
| resume_conversation_ai | Switch the AI back on for a conversation; the agent answers from the customer's next message, never an earlier one. | conversations:write | POST /v1/conversations/{id}/resume-ai |
| send_conversation_message | Send a text message to the conversation's customer (within the channel's 24-hour window where it has one). Needs an idempotency_key: reuse it only to retry the same message. | conversations:write | POST /v1/conversations/{conversation_id}/messages |
| send_conversation_template | Send an approved WhatsApp template to the conversation's customer: the only message official WhatsApp allows after its 24-hour window. Needs an idempotency_key. | conversations:write | POST /v1/conversations/{conversation_id}/messages |
| list_campaigns | List outbound and inbound campaigns. | campaigns:read | GET /v1/campaigns |
| get_campaign | Get one campaign with its settings and enrollment counts. | campaigns:read | GET /v1/campaigns/{id} |
| list_campaign_enrollments | List the contacts enrolled in one campaign with their status and lead response. | campaigns:read | GET /v1/campaigns/{campaign_id}/enrollments |
| list_goals | List agent evaluation goals and their targets. | goals:read | GET /v1/goals |
| get_goal | Get one evaluation goal. | goals:read | GET /v1/goals/{id} |
| list_competencies | List agent scoring competencies and their rubrics. | competencies:read | GET /v1/competencies |
| get_competency | Get one competency with its rubric. | competencies:read | GET /v1/competencies/{id} |
| list_webhooks | List the workspace's webhook endpoints with their events and last delivery status (signing secrets are never returned). | webhooks:read | GET /v1/webhooks |
| get_webhook | Get one webhook endpoint. | webhooks:read | GET /v1/webhooks/{id} |
| create_agent | Create an AI agent with its instructions and, optionally, the language it speaks (without one it runs in English, en-US: set it for every agent that speaks another language), the name it introduces itself with, its purpose and its call opening. Its first version (v1.0) is a draft that answers nobody until deploy_agent deploys it; voice, tools, knowledge and channels are set in the dashboard. | agents:write | POST /v1/agents |
| update_agent | Rename an agent or change its description (null clears it), at once. New instructions, language, identity name, purpose or call opening are saved together as one new draft version that answers only once deploy_agent deploys it. | agents:write | PATCH /v1/agents/{id} |
| deploy_agent | Make a version (version_id, default the newest) the one that answers on the agent's channels; the version deployed before goes back to draft. Confirm with the user first: customers talk to it right away. | agents:write | POST /v1/agents/{id}/deploy |
| archive_agent | Archive an agent and take it off every channel it is on: those channels stop answering with it at once, so connect another agent first. An archived agent disappears from the list and can't be fetched. | agents:write | DELETE /v1/agents/{id} |
| create_contact | Create a contact. phone_number must be international (+ and the country code); a number belongs to one contact per workspace. fields sets contact field values by key (see list_contact_fields). | contacts:write | POST /v1/contacts |
| update_contact | Change the contact details you send (null clears a name, the company or the notes; tags replaces the list; fields changes only the keys sent, null clears one). do_not_contact true opts the person out of every campaign call and message, false lifts it. A new phone number is refused while the contact's history is tied to the old one. | contacts:write | PATCH /v1/contacts/{id} |
| delete_contact | Permanently delete a contact that has no history. One with calls, conversations, appointments or campaign outreach is refused: opt it out with update_contact (do_not_contact) instead. | contacts:write | DELETE /v1/contacts/{id} |
| add_exclusions | Add up to 1000 international phone numbers to the exclusion list: no campaign calls or messages them again, their contacts are opted out and their pending campaign enrollments close (a number with no contact becomes an opted-out contact). Each number is answered excluded, already_excluded, own_number, duplicate or invalid. update_contact with do_not_contact false lifts an opt-out. | contacts:write | POST /v1/exclusions |
| list_contact_fields | List the workspace's contact fields (the custom data each contact can hold) in their order, with each one's key, type and choice options. | contacts:read | GET /v1/contact-fields |
| get_contact_field | Get one contact field by its id or its key. | contacts:read | GET /v1/contact-fields/{id} |
| create_contact_field | Add a contact field. Its key comes from the label and never changes (Interés is interes); a choice field needs its options. | contacts:write | POST /v1/contact-fields |
| update_contact_field | Rename a contact field (the key stays), change its type, move it, archive or restore it, or replace a choice field's options. A new type must fit every value contacts already have. | contacts:write | PATCH /v1/contact-fields/{id} |
| create_contact_import | Create a background import of a CSV file into a campaign or a contact list. Answers one upload URL per 32 MiB part, valid for two hours: PUT each part's bytes there (Content-Type: text/csv), then call start_contact_import. CSV text that fits in one MCP request goes through create_contact_import_from_csv instead. | contacts:write | POST /v1/contact-imports |
| create_contact_import_from_csv | Create a background import into a campaign or a contact list from CSV text (the whole file, up to about 60 KB: an MCP request is at most 64 KiB) and store it as the import's file; then call start_contact_import. A larger file goes through create_contact_import and its upload URLs. | contacts:write | POST /v1/contact-imports |
| renew_contact_import_upload | Fresh upload URLs (valid for two hours) for the parts of an import's file not uploaded yet, or uploaded with another size; parts already in place keep their bytes. | contacts:write | POST /v1/contact-imports/{id}/upload |
| start_contact_import | Start an import once its file is uploaded: the rows are imported in the background (follow it with get_contact_import), and a campaign's duplicates rule may replace the lead data of people already in it. Confirm with the user first: an active campaign starts calling people as their rows land. | contacts:write | POST /v1/contact-imports/{id}/start |
| cancel_contact_import | Stop an import for good and delete its file. Rows already imported stay imported. | contacts:write | POST /v1/contact-imports/{id}/cancel |
| get_contact_import | Get an import's status and progress: bytes read, and the rows added, updated, duplicated and refused (by reason). | contacts:read | GET /v1/contact-imports/{id} |
| get_contact_import_rejects | The rows an import refused, as CSV text (the file's columns, then why each row was refused and its row number): the first 32 KB, with truncated true when there is more (the whole file is GET /v1/contact-imports/{id}/rejects over REST). | contacts:read | GET /v1/contact-imports/{id}/rejects |
| create_contact_list | Create an empty contact list; add contacts with add_contact_list_members. | contact_lists:write | POST /v1/contact-lists |
| update_contact_list | Rename a contact list or change its description (null clears it). | contact_lists:write | PATCH /v1/contact-lists/{id} |
| delete_contact_list | Delete a contact list and its memberships. The contacts stay; a campaign that used it as its default list goes on without one. | contact_lists:write | DELETE /v1/contact-lists/{id} |
| add_contact_list_members | Add up to 500 existing contacts (by id) to a list; ids that aren't the workspace's contacts come back in not_found. A list that feeds an outbound campaign enrolls them there too (the key then needs campaigns:write): confirm with the user first, since an active campaign starts contacting them. | contact_lists:write | POST /v1/contact-lists/{list_id}/members |
| remove_contact_list_member | Remove one contact from a list (the contact stays). The enrollments the list created in its campaigns are removed while they have no outcome yet; a list linked to a campaign also needs campaigns:write. | contact_lists:write | DELETE /v1/contact-lists/{list_id}/members/{contact_id} |
| create_campaign | Create a draft campaign with the default workflow: call each contact with the agent on the outbound channel, then mark it interested when answered. Its steps, duplicates rule and test runs are edited in the dashboard. | campaigns:write | POST /v1/campaigns |
| update_campaign | Change a campaign's name, description (null clears it), outbound channel, calling hours or caps (rate_limits replaces the speed caps; null removes them). status paused stops an active campaign and archived archives one that is not active; activate it with start_campaign. | campaigns:write | PATCH /v1/campaigns/{id} |
| archive_campaign | Archive a campaign that is not active (pause it first). It stays readable and can't start again. | campaigns:write | DELETE /v1/campaigns/{id} |
| start_campaign | Activate a draft or paused campaign after the dashboard's start checks (a channel with a deployed agent, caps within the plan, a valid workflow): waiting contacts are queued and calling starts within the calling hours. Confirm with the user first: it calls real people and spends credits. | campaigns:write | POST /v1/campaigns/{id}/start |
| pause_campaign | Pause an active campaign: queued steps and calls not dialed yet are cancelled, and a call in progress finishes. start_campaign resumes it. | campaigns:write | POST /v1/campaigns/{id}/pause |
| enroll_campaign_contact | Enroll one person by phone number (the contact is found or created). On an active campaign the first call comes within the calling hours, usually within seconds; a draft or paused campaign holds them only when API import is on. The campaign's duplicates rule may update an existing enrollment. Confirm with the user first. | campaigns:write | POST /v1/campaigns/{campaign_id}/enrollments |
| import_campaign_contacts | Enroll up to 500 contacts in one request and get each row's result: enrolled, held, updated (the duplicates rule), skipped or rejected with its reason. Confirm with the user first: an active campaign starts calling them. | campaigns:write | POST /v1/campaigns/{campaign_id}/contacts/import |
| create_goal | Create a success goal for an agent, evaluated on one of its channels (channel_id can be left out when the agent is on exactly one), with optional targets per period. | goals:write | POST /v1/goals |
| update_goal | Change a goal's title, success criteria, channel (another of the agent's channels) or active switch; targets replaces every target. | goals:write | PATCH /v1/goals/{id} |
| delete_goal | Delete a goal and its targets. | goals:write | DELETE /v1/goals/{id} |
| create_competency | Create a competency the evaluator rates in each of the agent's evaluated conversations: what good looks like and 3 or 4 performance levels, lowest first. | competencies:write | POST /v1/competencies |
| update_competency | Change a competency's fields; rubric replaces every level. | competencies:write | PATCH /v1/competencies/{id} |
| delete_competency | Delete a competency. | competencies:write | DELETE /v1/competencies/{id} |
| create_webhook | Register an https endpoint for call, campaign or conversation events (conversation events also need conversations:read). The answer carries its signing secret once: give it to the user to verify the X-NuTalk-Signature header. Confirm the URL with the user: deliveries carry workspace data there. | webhooks:write | POST /v1/webhooks |
| update_webhook | Change a webhook endpoint's URL, events or description (null clears it), or pause its deliveries (is_active false); its secret stays the same. | webhooks:write | PATCH /v1/webhooks/{id} |
| delete_webhook | Delete a webhook endpoint; deliveries already queued for it are dropped. | webhooks:write | DELETE /v1/webhooks/{id} |
| list_clinic_locations | List the clinic's locations (name, time zone, address, phone, status) with their opening hours. | clinic_setup:read | GET /v1/clinic/locations |
| get_clinic_location | Get one clinic location with its opening hours. | clinic_setup:read | GET /v1/clinic/locations/{id} |
| create_clinic_location | Create a clinic location (a workspace's first location adopts the rooms that have none). | clinic_setup:write | POST /v1/clinic/locations |
| update_clinic_location | Change a clinic location's name, time zone, address, phone or status (active, inactive). | clinic_setup:write | PATCH /v1/clinic/locations/{id} |
| archive_clinic_location | Archive (remove) a clinic location: it leaves every flow, its history stays. | clinic_setup:write | DELETE /v1/clinic/locations/{id} |
| add_clinic_location_hours | Add one opening-hours range (weekday, open and close time) to a location. Opening hours bound the services nobody staffs. | clinic_setup:write | POST /v1/clinic/locations/{location_id}/hours |
| update_clinic_location_hours | Change one opening-hours range of a location (an id from the location's hours). | clinic_setup:write | PATCH /v1/clinic/location-hours/{id} |
| remove_clinic_location_hours | Delete one opening-hours range of a location. | clinic_setup:write | DELETE /v1/clinic/location-hours/{id} |
| list_clinic_rooms | List the clinic's rooms with their spots (the places that hold one appointment at a time); filter by location or status. | clinic_setup:read | GET /v1/clinic/rooms |
| get_clinic_room | Get one clinic room with its spots. | clinic_setup:read | GET /v1/clinic/rooms/{id} |
| create_clinic_room | Create a clinic room with its first spot (named as the room). | clinic_setup:write | POST /v1/clinic/rooms |
| update_clinic_room | Change a clinic room's name, location, room type, capacity or status (active, inactive). | clinic_setup:write | PATCH /v1/clinic/rooms/{id} |
| archive_clinic_room | Archive (remove) a clinic room. | clinic_setup:write | DELETE /v1/clinic/rooms/{id} |
| add_clinic_spot | Add a spot (a chair, bed or station that holds one appointment at a time) to a clinic room. | clinic_setup:write | POST /v1/clinic/rooms/{room_id}/spots |
| update_clinic_spot | Rename, reorder or switch off (inactive) one spot of a clinic room. | clinic_setup:write | PATCH /v1/clinic/spots/{id} |
| archive_clinic_spot | Archive (remove) one spot of a clinic room. | clinic_setup:write | DELETE /v1/clinic/spots/{id} |
| list_clinic_doctors | List the people who take clinic appointments (doctors, nurses, therapists, technicians, staff); filter by status, role or location. | clinic_setup:read | GET /v1/clinic/doctors |
| get_clinic_doctor | Get one clinic doctor. | clinic_setup:read | GET /v1/clinic/doctors/{id} |
| create_clinic_doctor | Add a doctor (or nurse, therapist, technician, staff) to Clinic Scheduling. They take appointments once they have weekly hours. | clinic_setup:write | POST /v1/clinic/doctors |
| update_clinic_doctor | Change a clinic doctor's details, defaults, time zone (their weekly hours move with it) or status (inactive stops new bookings). | clinic_setup:write | PATCH /v1/clinic/doctors/{id} |
| remove_clinic_doctor | Remove a doctor from Clinic Scheduling: refused while they have upcoming appointments; past appointments keep the name. | clinic_setup:write | DELETE /v1/clinic/doctors/{id} |
| get_clinic_doctor_weekly_hours | Get a clinic doctor's recurring week: the every-week ranges (hours) that set_clinic_doctor_weekly_hours replaces, and the rows it keeps (other_hours). | clinic_setup:read | GET /v1/clinic/doctors/{doctor_id}/weekly-hours |
| set_clinic_doctor_weekly_hours | Replace a clinic doctor's whole every-week schedule (a weekday left out has no hours). Lists the appointments the new week leaves outside its hours (never cancelled); dry_run lists them without saving. | clinic_setup:write | PUT /v1/clinic/doctors/{doctor_id}/weekly-hours |
| list_clinic_doctor_days_off | List a clinic doctor's blocked time (days off, time off), soonest first; by default what has not ended. | clinic_setup:read | GET /v1/clinic/doctors/{doctor_id}/days-off |
| add_clinic_doctor_day_off | Block a clinic doctor's time (instants, part of a day, or whole days on their clock). Appointments inside stay booked and are listed; dry_run previews. | clinic_setup:write | POST /v1/clinic/doctors/{doctor_id}/days-off |
| remove_clinic_day_off | Remove a clinic doctor's blocked time (an id from their days off); the time is bookable again at once. | clinic_setup:write | DELETE /v1/clinic/days-off/{id} |
| list_clinic_services | List the clinic's services (appointment types) with duration, buffers, staff rules, price and the facts the agents answer from. | clinic_setup:read | GET /v1/clinic/services |
| get_clinic_service | Get one clinic service. | clinic_setup:read | GET /v1/clinic/services/{id} |
| create_clinic_service | Create a clinic service (only the name is required). | clinic_setup:write | POST /v1/clinic/services |
| update_clinic_service | Change the fields sent of a clinic service; a list replaces the stored list. | clinic_setup:write | PATCH /v1/clinic/services/{id} |
| archive_clinic_service | Archive a clinic service so it can no longer be booked; appointments and history keep it. | clinic_setup:write | DELETE /v1/clinic/services/{id} |
| get_clinic_settings | Get the clinic-wide settings: several services at once, the AI agents' start grid and offer rules, and the round robin of new patients with each service's roster. | clinic_setup:read | GET /v1/clinic/settings |
| update_clinic_settings | Change the clinic settings sent (the AI's start grid and offer rules, simultaneous services, the round robin and its rosters). | clinic_setup:write | PATCH /v1/clinic/settings |
| list_clinic_appointments | List clinic appointments in start order (patient, service, doctor, time, status); filter by time window, doctor, service, location, status, patient phone or contact. | appointments:read | GET /v1/clinic/appointments |
| get_clinic_appointment | Get one clinic appointment. | appointments:read | GET /v1/clinic/appointments/{id} |
| find_clinic_availability | Find free times for a clinic service (or several at once) in a window of up to 31 days; each slot names the doctor, room and spot to book it with. | appointments:read | GET /v1/clinic/availability |
| book_clinic_appointment | Book a clinic appointment through the scheduling engine (a time from find_clinic_availability, the patient's name and phone). Send an idempotency_key to retry safely. | appointments:write | POST /v1/clinic/appointments |
| change_clinic_appointment_doctor | Move a scheduled clinic appointment to another doctor at the same time (the engine checks they are free and allowed). | appointments:write | PATCH /v1/clinic/appointments/{id} |
| update_clinic_appointment_details | Change a clinic appointment's patient details, notes or confirmation (for every service of the visit). | appointments:write | PATCH /v1/clinic/appointments/{id} |
| reschedule_clinic_appointment | Move a scheduled clinic appointment to a new start (validated by the scheduling engine); the patient is not notified from here. | appointments:write | POST /v1/clinic/appointments/{id}/reschedule |
| cancel_clinic_appointment | Cancel a scheduled clinic appointment (the whole visit), with a reason kept in its history. | appointments:write | POST /v1/clinic/appointments/{id}/cancel |
| set_clinic_appointment_status | Record how a visit went: completed or no_show; unconfirmed once it ended and nobody knows; scheduled to undo. | appointments:write | POST /v1/clinic/appointments/{id}/status |
| list_business_hosts | List the people who take business meetings (active, or archived), by name. | business_setup:read | GET /v1/business/hosts |
| get_business_host | Get one business host: name, email, title, time zone and status. | business_setup:read | GET /v1/business/hosts/{id} |
| list_business_host_availability | List a business host's weekly hours: each window's weekday (0 = Sunday … 6 = Saturday), start and end (HH:MM on the host's clock) and whether it is active. | business_setup:read | GET /v1/business/hosts/{id}/availability |
| list_business_host_days_off | List a business host's time off, oldest first; from/to keep the ones that overlap the range. | business_setup:read | GET /v1/business/hosts/{id}/days-off |
| list_business_closures | List company-wide closures (every host is blocked), oldest first; from/to keep the ones that overlap the range. | business_setup:read | GET /v1/business/closures |
| list_business_meeting_types | List business meeting types (length, buffers, start-time steps, location, notice, booking window) with the hosts who take each one (none listed = every active host). | business_setup:read | GET /v1/business/meeting-types |
| get_business_meeting_type | Get one business meeting type with the hosts who take it. | business_setup:read | GET /v1/business/meeting-types/{id} |
| list_business_host_calendar_connections | List a business host's connected calendar accounts (Google, Outlook, iCloud/CalDAV, iCal links): which calendars block their time, which one receives meetings, and any sync error. | business_setup:read | GET /v1/business/hosts/{id}/calendar-connections |
| list_business_calendar_connection_calendars | Ask the provider for a connected calendar account's calendars, with the ids to use when choosing which ones block the host and which one receives meetings. | business_setup:read | GET /v1/business/calendar-connections/{id}/calendars |
| create_business_host | Add a person who takes business meetings (name, and optionally email, title and time zone). Give them weekly hours before they can be booked. | business_setup:write | POST /v1/business/hosts |
| update_business_host | Change a business host's name, email, title or time zone (null clears email, title or time zone). | business_setup:write | PATCH /v1/business/hosts/{id} |
| archive_business_host | Archive a business host: they are no longer offered or booked; their booked meetings stay. | business_setup:write | DELETE /v1/business/hosts/{id} |
| add_business_host_availability | Add the same hours (start_time, end_time as HH:MM) on one or more weekdays (0 = Sunday … 6 = Saturday) for a business host. Refused when they overlap the host's existing hours on any of the days; a lunch break is two windows. | business_setup:write | POST /v1/business/hosts/{id}/availability |
| update_business_availability_window | Change one weekly window's weekday or hours, or switch it off (is_active false) or on. It must not overlap the host's other active windows that day. | business_setup:write | PATCH /v1/business/availability-windows/{id} |
| delete_business_availability_window | Remove one weekly window of a business host for good. | business_setup:write | DELETE /v1/business/availability-windows/{id} |
| add_business_host_day_off | Block a business host from starts_at to ends_at (ISO 8601 with an offset): no meeting is offered or booked with them then. | business_setup:write | POST /v1/business/hosts/{id}/days-off |
| update_business_day_off | Change a business host's day off (starts_at, ends_at, reason). | business_setup:write | PATCH /v1/business/days-off/{id} |
| delete_business_day_off | Remove a business host's day off: the time is bookable again. | business_setup:write | DELETE /v1/business/days-off/{id} |
| add_business_closure | Close the business from starts_at to ends_at (a holiday): no host is offered or booked then. | business_setup:write | POST /v1/business/closures |
| update_business_closure | Change a company-wide closure (starts_at, ends_at, reason). | business_setup:write | PATCH /v1/business/closures/{id} |
| delete_business_closure | Remove a company-wide closure. | business_setup:write | DELETE /v1/business/closures/{id} |
| create_business_meeting_type | Create a business meeting type: name, length, buffers, start-time steps, location, notice, booking window and the hosts who take it (none = every active host). | business_setup:write | POST /v1/business/meeting-types |
| update_business_meeting_type | Change a business meeting type's settings; host_ids replaces the hosts who take it (an empty list = every active host). | business_setup:write | PATCH /v1/business/meeting-types/{id} |
| archive_business_meeting_type | Archive a business meeting type: it is no longer offered or booked; its booked meetings stay. | business_setup:write | DELETE /v1/business/meeting-types/{id} |
| update_business_calendar_connection | Choose which of a connected account's calendars block the host's time, which one receives new meetings (Google and Outlook) and whether meetings are written to it. | business_setup:write | PATCH /v1/business/calendar-connections/{id} |
| disconnect_business_calendar_connection | Disconnect a calendar account from a business host (its tokens are removed; connecting it again is done in the dashboard). | business_setup:write | DELETE /v1/business/calendar-connections/{id} |
| list_business_appointments | List business meetings by start time; from/to keep the ones that overlap the range (from = now for upcoming ones); filter by host, meeting type, contact or status. | appointments:read | GET /v1/business/appointments |
| get_business_appointment | Get one business meeting: its time, host, invitee, location or meeting link, status and calendar sync. | appointments:read | GET /v1/business/appointments/{id} |
| find_business_availability | Find the free start times for a business meeting type on one day (date) or a range, optionally for one host; pass appointment_id to find times for moving that meeting. | appointments:read | GET /v1/business/availability |
| book_business_appointment | Book a business meeting at a start time returned by find_business_availability, with the invitee's name and phone (with country code). The host's calendar sends the invitee an invitation when an email is given. Pass an idempotency_key to retry safely. | appointments:write | POST /v1/business/appointments |
| reschedule_business_appointment | Move a business meeting to a new start time (found with find_business_availability and appointment_id). It keeps its host unless host_id names another; notify_invitee sends the invitee a WhatsApp note with the new time. | appointments:write | PATCH /v1/business/appointments/{id} |
| cancel_business_appointment | Cancel a confirmed business meeting (its calendar event and invitation are removed). It can't be undone. | appointments:write | PATCH /v1/business/appointments/{id} |
| set_business_appointment_outcome | Mark a business meeting that has started as completed or no_show (it frees the host's time and can't be undone). | appointments:write | PATCH /v1/business/appointments/{id} |
| update_business_appointment_invitee | Correct a business meeting's invitee details: name, phone (with country code), email, company or notes (null clears email, company or notes). | appointments:write | PATCH /v1/business/appointments/{id} |
| list_test_scenarios | List an agent's automated testing scenarios, newest first: each is a situation, the simulated customers who bring it (with their goal, personality and objections) and the goals and competencies it scores by default. | testing:read | GET /v1/agents/{agent_id}/test-scenarios |
| get_test_scenario | Get one test scenario with its simulated customers and its goal and competency links. | testing:read | GET /v1/test-scenarios/{id} |
| create_test_scenario | Write a test scenario for an agent: the situation (description) and the simulated customers who bring it, each with a name and a goal. goals and competencies (this agent's) are what a run scores when it names none. New scenarios run on messages, one conversation per customer. | testing:write | POST /v1/agents/{agent_id}/test-scenarios |
| update_test_scenario | Change the fields sent of a test scenario. customers, goals and competencies each replace the whole list: send every customer to keep. | testing:write | PATCH /v1/test-scenarios/{id} |
| delete_test_scenario | Delete a test scenario and its simulated customers. The test runs it ran stay readable, without their scenario. | testing:write | DELETE /v1/test-scenarios/{id} |
| start_test_run | Start a test run of a scenario: every simulated customer holds a conversation with the agent version (default: the deployed one), or with each of up to 4 versions to compare them, and each conversation is scored on the goals and competencies given (default: the scenario's). Nothing reaches a real customer. 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_test_run. | testing:write | POST /v1/test-scenarios/{scenario_id}/runs |
| list_test_runs | List an agent's test runs, newest first, with each run's status and how many of its conversations finished, failed and passed; filter by scenario or status. | testing:read | GET /v1/agents/{agent_id}/test-runs |
| get_test_run | Get one test run: each version's results (passed, failed, average turns), the winner of a comparison, and every simulated conversation with its transcript, the evaluator's summary and its score on each goal and competency. | testing:read | GET /v1/test-runs/{id} |
| rescore_test_run | Score a test run's finished conversations again (all of them, or conversation_ids) with its goals and competencies as they are now; each one's previous scores are replaced. | testing:write | POST /v1/test-runs/{id}/evaluate |
| send_test_chat_message | Write to the agent as a customer and get its reply from its real engine, with the tools it used (nothing is sent or booked, no credit is used; the workspace has a daily allowance of free test replies). Pass the chat_id answered to continue the same chat. | testing:write | POST /v1/agents/{agent_id}/test-chat |
| list_knowledge_sources | List the documents, pasted texts and web pages an agent answers from, newest first, with each one's status (pending, processing, ready, error) and size in chunks. | knowledge:read | GET /v1/agents/{agent_id}/knowledge |
| get_knowledge_source | Get one knowledge source of an agent with its status, error and warnings. | knowledge:read | GET /v1/agents/{agent_id}/knowledge/{source_id} |
| add_knowledge_text | Add a text the agent answers from (prices, policies, FAQs), with a title. It is chunked and embedded in the background: it answers once its status is ready. | knowledge:write | POST /v1/agents/{agent_id}/knowledge |
| add_knowledge_url | Add a public web page the agent answers from; crawl true also reads the same site's linked pages (up to max_pages, at most 50). Read in the background: it answers once its status is ready. Files are uploaded over REST. | knowledge:write | POST /v1/agents/{agent_id}/knowledge |
| delete_knowledge_source | Remove a knowledge source of an agent: the agent stops answering from it at once. | knowledge:write | DELETE /v1/agents/{agent_id}/knowledge/{source_id} |
| reingest_knowledge_source | Read a knowledge source again (a web page fetched anew); the agent keeps answering from the version it has until the new one is ready. | knowledge:write | POST /v1/agents/{agent_id}/knowledge/{source_id}/reingest |
| update_channel | Rename a channel, change its automatic evaluation (on or off, the share of conversations evaluated, a messaging channel's inactivity window) or, on a phone channel, whether it refuses callers with a hidden number. | channels:write | PATCH /v1/channels/{id} |
| set_channel_agent | Make an agent (deployed, not archived) the one that answers on a channel, with its deployed version; any other agent on it is taken off. Confirm with the user first: customers reach the agent right away. | channels:write | PUT /v1/channels/{id}/agent |
| remove_channel_agent | Take an agent off a channel (agent_id; without it, the channel's only agent). The channel stays connected and answers with no agent until one is put on it. | channels:write | DELETE /v1/channels/{id}/agent |
| set_channel_ai_mode | Switch who answers a messaging channel's chats: off (the team answers), testing (the AI answers only the channel's test numbers), live (the AI answers new conversations; live_scope all also existing chats from their next message), paused (the emergency stop) or resume (back to how it was before the pause). Owners and admins only. Confirm the exact change with the user first: live puts the AI in front of real customers. | channels:write | PUT /v1/channels/{id}/ai-mode |
| set_channel_scheduling | Set the only scheduling system the agent on a channel may book with: none, calcom, business, clinic or restaurant. The workspace must have the system, and the agent books one way only. | channels:write | PUT /v1/channels/{id}/scheduling |
How calls behave
- Pagination. List tools take
limit(1–100, default 20; 50 for a conversation's messages) andoffset; the result'spagination.has_moresays whether another page exists. - Errors. They come back as a tool result with
isError: true, so the assistant can correct itself:forbidden(missing scope) andinvalid_arguments(with each bad field) before the endpoint runs, and the REST error code with itshttp_statuswhen the endpoint refuses (for examplenot_found). An unknown tool name is a JSON-RPC error (-32602). A wrong or revoked key gets an HTTP 401 before any tool runs; some clients then try to sign in with OAuth and show an OAuth error instead: check the key withGET /v1/auth/verify. - Rate limits. Every MCP request (tool calls and protocol messages such as
tools/list) counts in an MCP bucket with the same limits as the REST API, and each tool call also counts like the REST request it runs (see Rate Limits). The response headers show the MCP bucket. - Transport. Send one JSON-RPC message per POST; batches are refused. The server keeps no session, so GET and DELETE answer 405. A request can be up to 64 KiB (a larger one answers 413), and the server speaks MCP 2025-06-18, 2025-03-26 and 2024-11-05.
- What it does not do. Placing calls, and uploading a contact file larger than one request (about 60 KB of CSV), are done over the REST API, with the same key. See what you can do, and where.
Calling a tool without a client
Useful to check a key or debug a client: post a JSON-RPC tools/call message.
curl https://www.nutalk.ai/v1/mcp \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_calls", "arguments": { "direction": "inbound", "limit": 5 } } }'{ "jsonrpc": "2.0", "id": 1, "result": { "isError": false, "content": [{ "type": "text", "text": "{\"data\":[…],\"pagination\":{…}}" }], "structuredContent": { "data": [{ "id": "…", "direction": "inbound", "status": "completed", … }], "pagination": { "total": 128, "limit": 5, "offset": 0, "has_more": true } } }}Security
What the assistant can and cannot do with your data.
- The server changes only what the key's Write scopes allow, and only once the key lets MCP set up the workspace: agents, contacts, lists, imports, scheduling, goals, competencies, tests, knowledge and channels, deletes included. Acting on conversations and anything that reaches people (messages, starting or enrolling into a campaign, webhooks, deploying an agent or putting it on a channel, bookings) also needs the key allowed to reach customers through MCP, which only owners and admins turn on. Tools that delete, overwrite or reach people are marked destructive or open-world, so clients that honour MCP tool annotations ask you before each call. Ask the assistant to show you each message before it sends it.
- Deletes and changes can also wait for an owner's or admin's approval, as they do on every new key: the tool then returns the pending change request (
data.change_request) and nothing changes until someone approves it in NuTalk (see Approvals). - Tool results include your customers' data: contact notes, call transcripts and summaries. Treat the assistant like any other place that data goes, and keep its approval prompts on for tools from other servers.
- Those texts can contain instructions written by someone else. The server tells the assistant to treat them as data and to confirm each change with you. With a Read only key, or with both switches off, injected text cannot use it to change your workspace; turn them on only for an assistant you supervise.
- API key secrets are never returned. A webhook's signing secret is returned once, when
create_webhookregisters the endpoint.
Ask focused questions
Assistants work best with narrow questions ("inbound calls yesterday that did not book") rather than full exports. For bulk data, use the REST API directly.