Each canonical purpose below is copied verbatim from the server manifest. Names, scopes, required top-level inputs, operation modes, and contract safeguards are generated from the same snapshot.
Your connected client’s
tools/list response is authoritative for the server it reaches. It includes the complete JSON Schema. If its count differs from this pinned snapshot, review the server revision before automating writes.The catalog still lists
create_client_tool, update_client_tool, assign_tool_to_workflow, configure_customer_api_tool, create_workflow_tool, and update_workflow_tool because those tools exist in tools/list. Creating, editing, or assigning a custom HTTP destination is temporarily not a supported public operation while Nexor hardens destination validation. Do not use these tools to configure a destination until this notice is removed.How to read the catalog
Read means the manifest declares no write scope. Write means the call may change state. A safeguard names only what the tool contract exposes; No contract flag is not permission to skip operator approval for a consequential action.
Session metadata (1)
Session metadata (1)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
whoami | Authenticated key | Read | None | No contract flag | Return the authenticated Nexor client identity and the scopes this API key was granted. Call this first to discover which actions are permitted. |
Account readiness (1)
Account readiness (1)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_account_readiness | Authenticated key | Read | None | No contract flag | MANDATORY FIRST-RUN TOOL. At the start of a new customer conversation, call this before the first substantive reply. Returns account/payment state, granted and missing setup scopes, saved onboarding context, agents/channels/template inventory, a factual readiness checklist, the safest next tools, external human touchpoints, and initial_dialogue with the exact opening behavior. Each appointment-capable agent carries a booking block (meeting_type, hosts with schedule/calendar state, activation blockers such as MEETING_TYPE_REQUIRED / BOOKING_AVAILABILITY_REQUIRED / CALENDLY_BINDING_REQUIRED, timezone warnings, and fix_tools). Follow initial_dialogue; do not ask for facts in resume_context, do not mutate while payment/account access is blocked, and do not claim the account is ready merely because dashboard onboarding was marked complete or because an agent is active: an active agent with booking blockers cannot book. |
Agents (12)
Agents (12)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_workflows | workflows:read | Read | None | No contract flag | List the authenticated client’s workflows (the funnels leads move through), including each agent’s master_workflow_id group assignment, enabled channels, and status ids. Archived (soft-deleted) agents are hidden by default; pass include_archived:true to see them too — each row then carries archived:true and a deleted_at timestamp so you can find an archived agent to restore with restore_workflow. |
get_workflow | workflows:read | Read | workflow_id | No contract flag | Fetch one workflow/agent with prompts/config, ordered status summaries, fields, channels and tool execution settings. Status summaries do not include their full gates/timeouts/transfers; call get_workflow_status for each status whose behavior you need to inspect. Use both reads for verification before activation. |
create_workflow | workflows:write | Write | name, goal_type, statuses | No contract flag | Create a new PAUSED workflow/agent. Provide an ordered list of statuses; each needs a key plus a name (or its label alias). If no is_initial/is_terminal flags are set, the first and last become entry/exit. Statuses accept the full stage config here — entry_hint, category, color, transition_rules, requires_all_fields/required_field_keys, timeout_config, transfer_config, pause_bot, assignment_config and the is_* flags — so a funnel can be fully configured in one call instead of follow-up update_workflow_status PATCHes. Unknown status fields are forwarded to the backend as-is. The statuses array is the COMPLETE funnel of YOUR stages: the backend seeds a default pipeline on create, so this tool reads the workflow back, deletes seeded statuses not in your list, and returns the workflow plus a pipeline_reconciliation report. Three core stages (new, contacted, engaged) and the virtual “discarded” stage ALWAYS exist on every agent and are NOT part of the statuses contract: they are never deleted and are listed under seeded_statuses_kept (reason core_stage/virtual) — do not try to remove them. The result carries a top-level status: “created” means the funnel is exactly what you asked; “created_with_warnings” means the report has REAL unresolved warnings (duplicate stage labels, non-core seeded stages that could not be removed, missing requested stages) — you MUST resolve those before telling the user the agent is built. Set prune_unrequested_statuses:false to keep seeded defaults. Pass timezone (IANA) and region_style: when omitted the backend anchors them to the ACCOUNT (country/host schedule), never to a Chilean default — read them back in the returned workflow. When the first-contact channel (channels[0] or the inferred one) has no verified resource on the account, the result carries a channel_not_verified warning with the fix tools (connect_whatsapp_number, provision_phone_number, register_email_sender). Idempotent on the backend (a guarded duplicate returns the existing id; stages holding leads are never deleted, only reported). For multi-agent or operational builds, first call describe_agent_configuration + review_agent_system_plan, resolve clarifications, and obtain explicit user approval of the final fingerprint. |
set_workflow_prompt | workflows:write | Write | workflow_id, global_prompt | No contract flag | Replace a workflow’s base (global) prompt — the business rules and persona the agent follows across channels. Read the current one with get_workflow first if you want to edit rather than overwrite. Channel-specific overrides are not touched. |
set_workflow_active | workflows:write | Write | workflow_id, active | No contract flag | Turn a workflow on (active) or off (paused). Workflows are created PAUSED — this is how you go live. Activating only starts cadence for enrolled leads not yet contacted; it never re-blasts leads already in progress. Appointment workflows need at least one active meeting type before they can activate. |
update_workflow | workflows:write | Write | workflow_id | No contract flag | Update a workflow’s identity, metadata, or agent-group assignment: rename it, change its description, set the objective of a ‘custom’ goal workflow (goal_statement), move it into an agent group (master_workflow_id), remove it from its group (master_workflow_id: null), or adjust the agent’s identity (language, timezone, region_style, agent_name, agent_role, company_name, begin_message). Pass only the fields you want to change. Use list_agent_groups to resolve a group name to its tenant-scoped id. |
set_channel_prompt | workflows:write | Write | workflow_id, channel, prompt | No contract flag | Set the per-channel prompt override for a workflow (e.g. WhatsApp vs email tone/format). Merges with existing channel prompts; doesn’t touch the global prompt. Use ‘call’ for the voice/call channel (‘retell’ is still accepted as an alias). |
get_workflow_prompt_history | workflows:read | Read | workflow_id | No contract flag | Read one workflow’s prompt version history — snapshots of global + channel prompts, what changed, by whom, and when. workflow_id must be the exact UUID returned by list_workflows/get_workflow; never pass a name, change id, or placeholder text. |
update_workflow_structure | workflows:write | Write | workflow_id | No contract flag | Add or edit a workflow’s basic funnel statuses and/or data fields. Upsert by key — new keys are added, existing ones updated; nothing is deleted. Status order is deliberately unsupported here: use reorder_workflow_statuses. After creating statuses, use update_workflow_status for entry/qualification gates, timeouts, transfers, pause behavior and trigger flags. To remove one safely, use get_pipeline_impact then delete_workflow_status. |
update_workflow_config | workflows:write | Write | workflow_id, config | No contract flag | Update a workflow’s behaviour config (the config bag: flags like gate_outbound_to_hours and other non-prompt settings). Merges top-level keys by default — pass only the keys you want to change; existing keys are preserved. Set replace:true only to overwrite the entire config object (rare). Does not touch the prompt, statuses, fields, or channels. Reserved keys are REFUSED here (400 RESERVED_CONFIG_KEY) because the runtime reads them by exact key and a misspelled key would merge silently: use set_payment_link for payment_link, set_host_assigner for host_assigner and set_external_booking for external_booking — those validate the shape and the referenced tools. |
set_opening_message | workflows:write | Write | workflow_id, channel | No contract flag | Set the agent’s FIRST message for one channel — the fixed opener sent/spoken verbatim before the agent improvises. channel ‘call’ (voice): message is the opening line spoken when the call connects (flat variables {{lead_first_name}}, {{lead_full_name}}, {{agent_name}}, {{company_name}}; ” clears it and restores the language-pack default); agent_speaks_first (default true) — set false to make the agent wait for the lead to speak, optionally with silence_ms (1000–30000, default 3000) before it opens anyway. channel ‘email’: a fixed first email subject + body sent verbatim (variables {{first_name}}, {{last_name}}, {{email}}, {{metadata.key}}); enabled:false with empty subject/body removes it so the agent composes the first email itself. channel ‘webchat’: message shown by the widget before the agent speaks. channel ‘sms’: message sent verbatim as the first outbound SMS (same variables as email; a legally required STOP notice is still appended for US numbers). channel ‘messenger’: message sent verbatim as the agent’s first reply in a new Messenger conversation. For call/webchat/sms/messenger, ” clears the opener so the agent composes it from the prompt. NOT free text: WhatsApp openers are Meta-approved templates → set_opening_templates; Instagram campaign openers are dashboard-only. Returns the persisted opener as read-back; read every channel’s opener with get_workflow → opening_messages. / Configura el PRIMER mensaje del agente por canal: ‘call’ (línea de apertura de voz + quién habla primero), ‘email’ (asunto + cuerpo fijos), ‘webchat’, ‘sms’ y ‘messenger’ (texto fijo del primer mensaje). WhatsApp usa plantillas aprobadas (set_opening_templates). |
set_payment_link | workflows:write | Write | workflow_id, source | No contract flag | Set (or clear) where a ‘Send Payment Link’ agent gets the link it sends to leads — the backing for the dashboard’s Payment link section. Pick the source: ‘fixed’ = one static URL for every lead (needs a valid http(s) url; optional label); ‘tool’ = a tool generates a per-lead link (needs the tool name; optional capture dot-path into the tool’s JSON response, e.g. ‘data.url’); ‘lead’ = read the link from a lead metadata field (needs metadata_key, e.g. ‘payment_url’); ‘none’ = clear the configured link. Use this instead of writing config.payment_link through update_workflow_config: this validates the shape and reads the saved value back, so a success here means it actually persisted. / Configura de dónde saca el link de pago un agente de ‘Enviar link de pago’: ‘fixed’ (URL fija http(s), label opcional), ‘tool’ (una herramienta genera el link; capture opcional), ‘lead’ (leer de metadata_key del lead) o ‘none’ (borrar). Devuelve el valor guardado como verificación. |
Agent tools (6)
Agent tools (6)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_workflow_tools | workflows:read | Read | workflow_id | No contract flag | List a workflow’s custom tools (name, method, url, active, stage gate, on-entry, call-once). |
create_workflow_tool | workflows:write | Write | workflow_id, name, description, url | No contract flag | Create a CUSTOM tool that lives only in THIS workflow (no catalog inheritance). Prefer create_client_tool + assign_tool_to_workflow when the tool should be reusable across workflows. name is the snake_case identifier the agent references (immutable). description tells the agent when to call it. parameters is a JSON Schema for the call arguments. Supports {{placeholders}} in url/headers ({{lead.metadata.X}}, {{lead_id}}, …). call_once caches the result per lead; auto_run_on_entry fires it when a lead enters the workflow; available_in_statuses gates it to funnel stages. |
update_workflow_tool | workflows:write | Write | workflow_id, tool_id | No contract flag | Edit a custom tool’s config (url, method, description, parameters, headers, timeout, flags). The name cannot change — delete + recreate to rename. |
set_workflow_tool_execution | workflows:write | Write | workflow_id, tool_id, mode | No contract flag | Choose exactly how an assigned workflow tool runs. mode=“on_entry” makes it a pre-agent/pre-execution hook: it fires as soon as a lead enters the workflow, before the first agent message, and therefore clears stage gating. mode=“agent_decides” lets the conversational agent call it and optionally gates it to specific status keys. These modes are mutually exclusive. call_once limits successful execution per workflow run; is_active is an independent kill switch. |
delete_workflow_tool | workflows:write | Write | workflow_id, tool_id | No contract flag | Remove a custom tool. Soft-disables by default (keeps audit); pass hard=true to delete permanently. |
set_tool_stage_gate | workflows:write | Write | workflow_id, tool_id | No contract flag | Gate a tool to specific funnel stages: it becomes callable only when the lead is in one of available_in_statuses. Pass null to clear the gate (callable anywhere). |
Reusable client tools (6)
Reusable client tools (6)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_client_tools | workflows:read | Read | None | No contract flag | List the client’s reusable tool catalog. Tools here can be assigned to multiple workflows and stay in sync. |
create_client_tool | workflows:write | Write | name, description, url | No contract flag | Create a reusable tool in the client catalog. This is the RECOMMENDED low-level path for an existing customer API operation that should be available across workflows; configure_customer_api_tool is the guided create-and-assign path. name is the immutable snake_case function identifier. description is loaded into the agent and must say when/how to call the API without containing credentials. parameters is the agent-visible JSON Schema. Use {{env.KEY}} in url/headers for secrets configured with set_function_environment_variable; lead/context placeholders are also supported. |
configure_customer_api_tool | workflows:write | Write | name, purpose, when_to_use, url | No contract flag | Turn one operation from an API the customer owns or already uses into a reusable Nexor agent tool, then optionally assign it to workflows. Creates the catalog tool, composes scoped agent instructions from purpose/when-to-use/when-not-to-use/response rules, and configures per-workflow stage gates, call-once behavior, and response-field allowlisting. Model one focused API operation per tool. Store credentials with set_function_environment_variable and reference them as {{env.KEY}} in headers; NEVER put credentials in purpose/instructions/parameter descriptions. This creates a new immutable name; use list_client_tools + update_client_tool when that name already exists. |
update_client_tool | workflows:write | Write | tool_id | No contract flag | Edit a catalog tool’s config. Changes propagate to every workflow that has assigned (inherited) this tool. The name cannot change — delete + recreate to rename. |
delete_client_tool | workflows:write | Write | tool_id | No contract flag | Remove a tool from the client catalog. |
assign_tool_to_workflow | workflows:write | Write | client_tool_id, workflow_id | No contract flag | Assign a catalog tool to a workflow; it inherits and stays in sync with the catalog tool. Idempotent — re-assigning an already-assigned tool returns the existing assignment. |
Leads (17)
Leads (17)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_leads | leads:read | Read | None | No contract flag | Page through the authenticated client’s leads with optional search, workflow and human-assignee filters. A workflow_id includes active and settled/inactive membership by default, matching get_pipeline_impact; pass run_state=active only for current runs. Every lead includes assigned_to plus assigned_user when assigned; use list_team (people:read) for the directory. For recurring polls, pass updated_since with sort_by=updated_at sort_dir=asc and resume from the max updated_at seen. |
get_lead | leads:read | Read | lead_id | No contract flag | Fetch one lead by id, including its assigned human owner (assigned_to and assigned_user id/name, or null), field values, per-channel engagement and active workflow run. Use list_team (people:read) for the complete user directory. |
get_leads | leads:read | Read | lead_ids | No contract flag | Batch read many leads by id in ONE call (1-500 per call), each with the same field values, per-channel engagement and workflow runs as get_lead. Always prefer this over calling get_lead in a loop — N sequential get_lead calls burn the rate limit. Unknown ids come back in missing_ids. |
get_lead_history | leads:read | Read | lead_id | No contract flag | Read what was actually said or sent to ONE lead. The unified timeline returns full WhatsApp and SMS text, email subject/body plus delivery details, and call summary/transcript, with direction and timestamps; it also includes activities, meetings and conversions when unfiltered. Use channel=call,whatsapp,email,sms (one or comma-separated) to isolate communication content. Paging mode uses limit/offset and order_by; when pagination.has_more is true, keep paging before claiming the history is complete. Conversation-window mode uses anchor_ts with before/after (0-20 each) to inspect the messages around one moment. Use list_transcripts/get_transcript only for meeting transcripts, not phone calls. |
upsert_leads | leads:write | Write | leads | No contract flag | Create or update one or more leads (idempotent upsert on email/phone). When workflow_id is provided, enrollment has MOVE semantics: every other active run is deactivated before the target run is created, and the result is verified by reading each successful lead back. Use assign_lead_to_workflow for an already-known lead id. WARNING: first_message sends a real outbound message immediately (no agent composition) and also requires messages:send; first_channel only pins the first cadence channel. |
update_lead | leads:write | Write | lead_id | No contract flag | Update one lead. Put identity/contact values in standard fields (first_name, last_name, email, phone), and other agent-readable context in metadata. Metadata is shallow-merged and becomes available to the agent on its next execution; it does not change contact destinations or send data externally. Archived leads are rejected. |
sync_lead_tags | leads:write | Write | tags | No contract flag | Set the exact tag set for a lead (empty clears all tags). Identify the lead by id or email. |
assign_lead_to_workflow | leads:write | Write | lead_id, workflow_id | No contract flag | Move a lead into a workflow: deactivates the current run, creates a fresh run and starts its cadence (may trigger outbound contact). |
stop_automation | leads:write | Write | lead_id | No contract flag | Human takeover ON for a lead: pause its cadence and cancel pending scheduled tasks. The active workflow is resolved automatically; only pass workflow_id if the lead is in more than one. |
resume_automation | leads:write | Write | lead_id | No contract flag | Human takeover OFF for a lead; optionally reactivate its cadence (may resume outbound contact). |
set_lead_status | leads:write | Write | lead_id, status_key | No contract flag | Force a lead’s funnel status (move it forward or back). The active workflow is resolved automatically; pass workflow_id only if the lead is in more than one. |
bulk_set_lead_status | leads:write | Write | lead_ids, status_key | confirm_transfer_side_effects: true | Set the funnel status for many leads at once (1-500 per call). Applies the same per-lead status change (with history) as set_lead_status; leads that can’t be updated come back in failed with a reason. When the target status auto-messages (transfer_config), those leads are withheld with reason transfer_side_effects_require_confirmation — confirm with the operator, then re-call with confirm_transfer_side_effects=true to include them. |
bulk_assign_leads_to_workflow | leads:write | Write | lead_ids, workflow_id | No contract flag | Move many leads (1-500 per call) into a workflow: each gets its current run deactivated and a fresh run with cadence started (may trigger outbound contact). Failures come back per lead in failed. |
bulk_archive_leads | leads:write | Write | lead_ids | No contract flag | SOFT delete (archive) many leads at once (1-500 per call). REVERSIBLE: the leads disappear from active views and their automation stops, but the records are kept and can be restored. This does NOT permanently erase anything — for that, use bulk_hard_delete_leads. When the operator asks to “delete”/“remove”/“borrar”/“eliminar” leads and it is not crystal clear whether they mean archive (reversible) or permanent erase (irreversible), ASK before calling either tool. Confirm before calling. Failures come back per lead in failed. |
delete_lead | leads:write | Write | lead_id | No contract flag | SOFT delete (archive) one lead. REVERSIBLE: the record is kept and can be restored; this does NOT permanently erase it — for that, use hard_delete_lead. When the operator asks to “delete”/“remove”/“borrar”/“eliminar” a lead and it is not crystal clear whether they mean archive (reversible) or permanent erase (irreversible), ASK before calling either tool. |
hard_delete_lead | leads:delete | Write | lead_id | confirm: true | PERMANENTLY delete one lead. IRREVERSIBLE AND NOT RECOVERABLE: the lead and ALL of its data — every conversation, message, meeting, call, field value, note, and task — are erased forever. There is no undo and no restore. This is NOT archiving (use delete_lead for reversible archiving). Preview-then-confirm: call with confirm:false (or omit it) to get a { status:‘needs_confirmation’ } preview that writes NOTHING; only call again with confirm:true after the operator explicitly confirms permanent erasure of this exact lead. When “delete”/“remove”/“borrar”/“eliminar” is ambiguous between archive and permanent erase, ASK first — never assume hard delete. |
bulk_hard_delete_leads | leads:delete | Write | lead_ids | confirm: true | PERMANENTLY delete many leads at once (1-500 per call). IRREVERSIBLE AND NOT RECOVERABLE: each lead and ALL of its data — every conversation, message, meeting, call, field value, note, and task — are erased forever. There is no undo and no restore. This is NOT archiving (use bulk_archive_leads for reversible archiving). Preview-then-confirm: call with confirm:false (or omit it) to get a { status:‘needs_confirmation’ } preview that writes NOTHING; only call again with confirm:true after the operator explicitly confirms permanent erasure of these exact lead ids. When “delete”/“remove”/“borrar”/“eliminar” is ambiguous between archive and permanent erase, ASK first — never assume hard delete. Failures come back per lead in failed. |
Rules (9)
Rules (9)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_reminder_catalog | rules:read | Read | None | No contract flag | The reminder contract for THIS account: channels with per-account availability (available_for_account + reason, e.g. retell needs an active callable number, sms an SMS-enabled number, whatsapp an active WhatsApp number, email a verified sender), trigger events the runtime actually dispatches (with which ones are reconciled onto existing bookings), delay references and default templates. Call it before set_reminder_rule when choosing a channel; pass workflow_id for the workflow-scoped answer. |
list_reminder_rules | rules:read | Read | None | No contract flag | List lead-facing reminder rules (booking confirmations, pre-meeting reminders). |
set_reminder_rule | rules:write | Write | trigger_event, template_name | No contract flag | Create or update a lead reminder rule (idempotent). Channels: whatsapp | retell | sms | email — whether each one can actually send on this account comes from get_reminder_catalog (available_for_account); a rule on an unavailable channel is stored but the response carries warning ‘channel_not_available’ until the sender is provisioned. trigger_event ‘call_analyzed’ fires post-CALL, not post-meeting; there is no ‘reschedule’ event (a reschedule re-fires event_created). delay_minutes is signed: negative with delay_reference=‘event_start’ means before the meeting. Example: {trigger_event:‘event_created’, channel:‘whatsapp’, template_name:‘confirm_booking’, delay_minutes:0}. |
list_host_reminder_rules | rules:read | Read | None | No contract flag | List host (sales rep) notification rules. Email-only. Note: cancellation notifications are on by default and may not appear here. |
set_host_reminder_rule | rules:write | Write | trigger_event, template_name | No contract flag | Create or update a host booking-notification rule (notify the rep by email when a meeting is booked; idempotent). Omit workflow_id for a global rule. Note: host cancellation and reschedule alerts are already sent by default and are not configured here. |
list_recontact_rules | rules:read | Read | None | No contract flag | List re-engage (recontact) rules that drive the AI scanner re-contacting stale leads. Each rule carries cadence_owned (true when its stage belongs to the initial outreach cadence, so it can never be active). |
set_recontact_rule | rules:write | Write | name, stale_after_hours | No contract flag | Create or update a re-engage rule (idempotent). The initial outreach cadence OWNS the stages new, contacted and the workflow’s initial stage: an active rule there returns 409 CADENCE_OWNED_STAGE (fix: target a later custom stage such as engaged/proposal_sent, or send is_active:false). prompt_hint is a binding instruction to the AI. stale_reference defaults to ‘last_touchpoint_at’ (recommended; ‘last_response_at’ silently freezes silent leads). |
list_status_timeout_rules | rules:read | Read | None | No contract flag | List status auto-timeout configs (auto-transition stagnant leads after a delay). |
set_status_timeout_rule | rules:write | Write | workflow_id, status_key | No contract flag | Set or clear the auto-timeout on a workflow status. target_status_key must be an existing status key in the SAME workflow. Pass clear:true to disable. |
People and invitations (4)
People and invitations (4)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_team | people:read | Read | None | No contract flag | List the authenticated client’s active team members (sales reps / hosts) with user id, name, email and CRM external id. Use these user ids with assigned_to filters; lead and conversation reads expose the matching assigned_user by default. |
list_pending_invitations | people:read | Read | None | No contract flag | List outstanding invitations (not yet accepted, not cancelled, not expired). Consult this before inviting to avoid duplicates. |
invite_user | invites:write | Write | email, role | dry_run preview | Invite a person to the client’s organization by email and role. Idempotent: a still-valid pending invite to the same email is reused, not re-sent. Returns status: created | reused | already_member. |
revoke_invitation | invites:write | Write | None | No contract flag | Cancel a pending invitation by id or email (preserves audit; does not hard-delete). |
Messaging (6)
Messaging (6)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_whatsapp_templates | leads:read | Read | None | No contract flag | List the client’s WhatsApp templates with components, variables, status, internal_type, language, quality_score, rejection_reason, is_in_pool and sync timestamps, plus totals { total, by_status }. Defaults to APPROVED; pass status=ALL for a complete terminal-readable approval dashboard, or select PENDING/REJECTED/etc. Filters: internal_type, language prefix, and q (name/body substring). For fresh Meta state call check_whatsapp_template_approvals. |
get_whatsapp_template | whatsapp:read | Read | None | No contract flag | Fetch one WhatsApp template in full detail: status, internal_type, language, category, components, variables, quality_score, rejection_reason (when Meta rejected it), whether it is in a cadence pool, and sync timestamps. Look it up by template_id (from list_whatsapp_templates) or by exact name. |
review_whatsapp_template_drafts | whatsapp:read | Read | templates | No contract flag | Read-only preflight for one or many WhatsApp template drafts. Use it as the conversational back-and-forth loop: generate drafts from the user requirement, call this tool, show errors/warnings and normalized_templates, revise, and repeat. It verifies the connected WhatsApp account, Meta formatting, examples for every variable, buttons, duplicate names/bodies, and cadence purpose. When all_valid=true it returns a review_id; show the exact normalized_templates to the user and get final sign-off before create_whatsapp_templates_batch. |
create_whatsapp_templates_batch | whatsapp:write | Write | templates, review_id, confirm | confirm: true | Submit 1-20 reviewed WhatsApp templates to Meta in one confirmed operation. REQUIRED FLOW: review_whatsapp_template_drafts → show normalized drafts and review_id → obtain explicit user sign-off → call this tool with those exact drafts, review_id, confirm=true. Never invent confirmation. Meta writes are not atomic, so the result reports every accepted, already-existing, or failed item; then use check_whatsapp_template_approvals to refresh approval states. |
create_whatsapp_template | whatsapp:write | Write | name, body | No contract flag | Create a new WhatsApp template and submit it to Meta for approval (usually minutes to 48h). Templates are AGENT-SPECIFIC: first resolve the target agent (ask, or list_workflows) and read it with get_workflow so the copy matches that agent’s persona, goal, product, and language — never write generic copy. After creation, wire the template into THAT agent’s cadence with set_whatsapp_template_purpose plus set_template_pool / set_opening_templates, or it is never auto-used. Body text uses {{variable}} placeholders — variables cannot start/end the text nor be consecutive. Name must be lowercase_with_underscores. PREFER the canonical variable names Nexor auto-resolves at send time: {{nombre}} (lead first name), {{agente}} or {{ejecutivo}} (agent name), {{empresa}} (company), {{producto}} (product), and for meeting reminders {{fecha}}, {{hora}}, {{asesor}}, {{link_reunion}}. Other names still work but fall back to LLM resolution. channel_id is optional when the client has a single WhatsApp business account (use list_whatsapp_numbers to disambiguate). |
send_message | messages:send | Write | lead_id, channel | No contract flag | Send an outbound message to a lead. WhatsApp/email/sms/imessage accept free text; outside the 24h WhatsApp window you must pass an approved template_id instead of text. channel=“sms” sends via the workflow’s SMS-enabled number. channel=“imessage” sends a blue-bubble iMessage via the workflow’s bound iMessage number (free text, no templates). channel=“call” triggers an outbound call. workflow_id is optional — it is resolved from the lead’s active run when omitted. |
Account (8)
Account (8)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_lead_meetings | leads:read | Read | lead_id | No contract flag | List a lead’s calendar events with host and meeting type. |
create_meeting | meetings:write | Write | lead_id, starts_at, ends_at | No contract flag | Log a booked meeting against an existing lead (no Google Calendar sync). Idempotent via external_event_id. |
ingest_meeting_notes | meetings:write | Write | external_meeting_id, provider | No contract flag | Attach an external transcript / summary / action items to an existing meeting. The meeting is resolved via calendar_event_id, external_calendar_provider + external_calendar_event_id, meeting_url, or lead_email (+ optional meeting_date) — at least one is required. external_meeting_id only dedupes the notes (idempotency), it does NOT locate the meeting. Reserved providers (fireflies, diio, gemini, retell) are rejected by the backend. |
list_conversion_types | leads:read | Read | None | No contract flag | List the client’s active conversion types (use these ids with create_conversion). |
set_conversion_type | leads:write | Write | name | No contract flag | Create or update a conversion type by name (idempotent upsert). Conversion types define what counts as a conversion (e.g. “Cuenta abierta”, “Primer depósito”) and are required before create_conversion. Never deletes — deactivate with is_active:false; full removal is dashboard-only. |
create_conversion | leads:write | Write | conversion_type_id, amount | No contract flag | Record a conversion event for a lead (may trigger an async workflow transfer). Needs a conversion_type_id (see list_conversion_types) and an amount, plus exactly one lead identifier. |
list_conversions | leads:read | Read | None | No contract flag | List conversion events for the client. |
list_campaigns | leads:read | Read | None | No contract flag | List the client’s campaigns. |
Background jobs (10)
Background jobs (10)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_background_jobs | workflows:read | Read | None | No contract flag | List the client’s background jobs (scheduled or forced lead automations). |
get_background_job | workflows:read | Read | job_id | No contract flag | Fetch a background job by id with its recent executions. |
create_background_job | workflows:write | Write | name, steps | dry_run execution mode | Create a background job: a scheduled/forced automation that filters leads and runs steps on each. trigger_type “cron” needs a cron_expression; “api” runs only when forced (run_background_job). A step can evaluate a date/condition — on a metadata key (evaluate_metadata), a collected funnel field (evaluate_field), or a tag (evaluate_tag) — and act, OR run an action directly. Actions include sending a WhatsApp template (send_message), set_lead_status, assign/transfer workflow. lead_filters/workflow_filters select candidates; max_leads_per_cycle caps each run (≤500); cooldown_minutes avoids re-acting on the same lead; dry_run logs without acting. See the steps field for the full vocabulary and lead_filters / workflow_filters for how candidates are selected. Filters and steps are validated server-side: an unresolvable one is rejected with code invalid_filters / invalid_steps and a details[] naming each problem. Common use: send a survey template once to everyone sitting in a given status. |
update_background_job | workflows:write | Write | job_id | dry_run execution mode | Update a background job (any subset of its config). Filters and steps are validated server-side (invalid_filters / invalid_steps with details[]). See lead_filters / workflow_filters for candidate selection and steps for the action vocabulary (send_message, evaluate_field, set_lead_status, …). |
set_background_job_active | workflows:write | Write | job_id, active | No contract flag | Pause or activate a Background Job without changing the rest of its configuration. Keep dry_run:true until execution logs are verified. |
delete_background_job | workflows:write | Write | job_id | No contract flag | Delete a background job (cascades its execution history). |
run_background_job | workflows:write | Write | job_id | Operator approval in instructions | Force-run a Background Job now (bypasses schedule/active timing). A non-dry-run job can mutate real leads; inspect config and get explicit operator confirmation first. |
list_background_job_executions | workflows:read | Read | job_id | No contract flag | A background job’s recent execution history (counts of candidates/acted/errored per run). |
get_background_job_execution | workflows:read | Read | execution_id | No contract flag | Get one Background Job execution with every per-lead action, tool response, condition result, mutation result, skip, and error. |
analyze_background_job_template | workflows:read | Read | template_name | No contract flag | Inspect an APPROVED WhatsApp template used by send_message: shows each {{variable}}, whether it resolves automatically or via a workflow tool, and configuration errors. |
Reports (8)
Reports (8)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
export_leads_report | reports:read | Read | workflow_ids | No contract flag | Create a complete CSV export of every active, non-test lead in one or more exact workflows, including each lead current stage. Use for requests such as “todos los leads”, “exporta”, “descarga” or “dame un CSV”. First resolve workflow names to their UUIDs with list_workflows, then pass those UUIDs here. Returns a signed download URL. |
get_funnel_report | reports:read | Read | None | No contract flag | Aggregate campaign/funnel report: how many leads sit at each status, and how many were contacted/connected, per workflow (or across all workflows). Use for “how many leads are at each stage” style questions. |
get_field_distribution | reports:read | Read | field_key | No contract flag | Distribution of a captured lead field (e.g. a disqualification reason) with counts per value and an example evidence quote. Ideal for “top reasons why…” style questions. |
get_call_stats | reports:read | Read | None | No contract flag | Call statistics (dials, connected, discovery calls, duration) for a date range, totaled or broken down by day. |
get_lead_report | reports:read | Read | lead_id | No contract flag | Full report bundle for ONE lead: profile, current status, captured field values with evidence, insights, a summary of each call and scheduled tasks — all in one call. Prefer this over get_lead + get_lead_history when the goal is writing a report about a lead. |
get_engagement_metrics | reports:read | Read | None | No contract flag | Engagement snapshot for a date range (default: trailing 7 days): total leads, new leads, meetings booked, and outbound/inbound message counts. Test leads are excluded. Ideal for “how did we do this week” or activity-overview questions; for per-status funnel breakdowns use get_funnel_report. |
get_seller_assignment_metrics | reports:read | Read | None | No contract flag | Analyze how the client’s leads are distributed across human sellers. Returns assignment coverage (total, assigned, unassigned and assignment rate) plus one row per seller with assigned leads, share of assigned leads, current funnel-stage breakdown, unanswered conversations, meetings booked/completed, conversions, conversion value and conversion rate. Assignment is a CURRENT-OWNER snapshot: historical activity is attributed to the lead’s current assigned_to seller, not necessarily the seller who owned it when the activity happened. Date filters select leads by created_at and activity by each event’s timestamp. Use list_team (people:read) when you need the complete user directory. |
analyze_lead_fields | reports:read | Read | None | No contract flag | Statistical analysis of captured lead field values: numeric fields get min/max/mean/median/p25/p75/stddev/outlier_count, boolean/categorical fields get top-15 value frequencies. Filter by workflow, specific field keys, lead statuses, or an explicit lead list (max 50). Ideal for “what is the average budget of qualified leads” or comparing field values across a segment. If your filters match nothing, the response includes available_field_keys as a hint. For a simple one-field value distribution with evidence quotes, prefer get_field_distribution. |
Meetings (8)
Meetings (8)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_meetings | leads:read | Read | None | No contract flag | Page through the client’s meetings (booked calls/appointments) with lead, workflow, time and status for each. Ideal for questions like “what meetings are scheduled this week”, “which meetings were no-shows”, or listing a single lead’s meetings. For aggregate counts/rates use get_meeting_stats instead. |
get_meeting_stats | reports:read | Read | None | No contract flag | Aggregate meeting statistics for a date range: totals per status (completed, no_show, cancelled, scheduled, rescheduled) plus show_rate, completion_rate and no_show_rate (0-1, null when the denominator is 0). Ideal for “what is our show rate” or “how many meetings did we book last month” questions. For individual meetings use list_meetings. |
set_meeting_status | meetings:write | Write | meeting_id, status | No contract flag | Record the outcome of a meeting: ‘no_show’ (the lead did not attend) or ‘completed’. Runs the same side effects as the dashboard — the no-show re-engagement rule (when configured), the meeting webhook, Cloud Function events and the lead timeline — then reads the meeting back with its provider sync status. Idempotent: re-marking the same outcome is a no-op. meeting_id is the calendar event id from list_meetings / list_lead_meetings. To cancel a meeting use cancel_meeting; to move it use reschedule_meeting. |
cancel_meeting | meetings:write | Write | meeting_id | confirm: true | Cancel a booked meeting. DESTRUCTIVE and lead-facing: deletes the event from the host’s calendar (provider-first — if that fails nothing is cancelled), releases the slot, cancels lead and host reminders, emails the lead a cancellation and notifies the host, fires the meeting webhook and any cancellation re-engagement rule. Preview-then-confirm: call with confirm:false (or omit it) to get a { status:‘needs_confirmation’ } preview that writes NOTHING; only call again with confirm:true after the operator explicitly confirms cancelling this exact meeting. Meetings owned by an external provider (Calendly, GoHighLevel…) come back { status:‘skipped’ }. Reads the meeting back afterwards. |
get_workflow_slots | workflows:read | Read | workflow_id, from, to | No contract flag | Free booking slots for a workflow’s hosts between two dates, in the workflow’s timezone — the same slot engine the dashboard meetings board uses (host calendars, availability schedules, buffers, tier rules). Use it to verify a host produces slots and to pick a starts_at for book_meeting / reschedule_meeting. meeting_type_id defaults to the workflow’s first active meeting type; host_id restricts to one host (must be an active agent of the workflow). |
book_meeting | meetings:write | Write | workflow_id, lead_id, host_id, starts_at | confirm: true | Book a meeting for a lead with a host at starts_at, with the SAME side effects as booking from the dashboard: calendar event on the host’s connected calendar (Google/Outlook/Calendly), conferencing link, lead invite + confirmation email (unless invite_lead:false), host notification and reminders, availability block, and the workflow status transition. Pick starts_at from get_workflow_slots. A slot already taken returns CONFLICT with same-day alternatives. Pass the same idempotency_key to retry safely — a replay returns the original booking without booking again. Preview-then-confirm: when the lead will be invited, call with confirm:false (or omit it) to get a { status:‘needs_confirmation’ } preview that writes NOTHING; call again with confirm:true only after the operator explicitly approves. The response includes provider_sync — check its status; ‘sync_failed’ means the row exists but the calendar write failed. |
reschedule_meeting | meetings:write | Write | meeting_id | confirm: true | Move a booked meeting to a new time (starts_at, or a slot object from get_workflow_slots). Provider-aware: the host’s calendar event is updated first and the meeting link is kept when the host is unchanged; keep_host:false with a host_id rebooks on the new host (new link). The lead receives a reschedule email and the host is notified. Preview-then-confirm: call with confirm:false (or omit it) to get a { status:‘needs_confirmation’ } preview that writes NOTHING; call again with confirm:true only after the operator explicitly approves. Meetings owned by an external provider come back CONFLICT/MANAGED_EXTERNALLY. Returns the meeting read-back with provider_sync. |
change_meeting_host | meetings:write | Write | meeting_id, host_id | confirm: true | Reassign a booked meeting to another host (active agent of the same workflow) without changing the time. Provider-aware: the calendar event is moved to the new host’s calendar first (new organizer/link where the provider requires it); if that move fails nothing changes. The lead’s invite is updated. Preview-then-confirm: call with confirm:false (or omit it) to get a { status:‘needs_confirmation’ } preview that writes NOTHING; call again with confirm:true only after the operator explicitly approves. Returns the meeting read-back with provider_sync. |
Transcripts (2)
Transcripts (2)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_transcripts | leads:read | Read | None | No contract flag | Page through meeting transcripts: per row the lead, meeting date, title, a short summary (<=500 chars), whether it has action items, and duration. Ideal for “what was discussed in recent meetings” or finding a transcript by keyword before drilling in with get_transcript. |
get_transcript | leads:read | Read | transcript_id | No contract flag | Fetch ONE meeting transcript by id: lead, meeting date, title, full summary, action items and duration. Set include_full_text/include_sentences only when you actually need the verbatim conversation — they can be very large. Use list_transcripts first to find the id. |
Conversations (1)
Conversations (1)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
search_conversations | leads:read | Read | None | No contract flag | Search messages across ALL leads at once (get_lead_history covers a single lead). Results include the lead’s assigned human owner by default (assigned_to plus assigned_user id/name, or null); filter by assigned_to or unassigned_only, and use list_team (people:read) to resolve the full user directory. Two modes: normal mode returns matching messages with lead, channel, direction and a content preview — ideal for “who mentioned pricing this week”; unanswered=true instead returns leads whose LAST message is an unanswered inbound (waiting on a reply) — ideal for “which leads are waiting on us”. |
Integrations (1)
Integrations (1)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_integration_status | workflows:read | Read | None | No contract flag | One-call health summary of the client’s integrations: connected CRMs (provider, status, connected_at, last_synced_at, error), counts of WhatsApp numbers, phone numbers and email senders, calendars[] (one per host calendar connection: user_id, name, provider google|outlook|calendly, status active|expired|revoked|error|needs_reauth, role, calendar_email, last_error, connected_at) and booking_providers[] (one per appointment-capable agent: provider native|calendly|gohighlevel|external_booker, binding summary, host_count, excluded_hosts = Calendly hosts with no event type bound, which booking silently skips). Ideal for “is our CRM synced”, “is the calendar connected” or “why can’t the agent book” questions; drill into a channel with its own list tool (list_whatsapp_numbers, list_phone_numbers, list_email_senders). |
Phone numbers (7)
Phone numbers (7)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
search_available_numbers | numbers:read | Read | country | No contract flag | List phone numbers available to buy in a country (ISO code, e.g. “CL”, “MX”). Read-only preview — buying one is provision_phone_number. |
list_phone_numbers | numbers:read | Read | None | No contract flag | List the client’s VOICE/SMS phone numbers with their scope (client vs workflow), the workflow each is bound to, SMS status, and whether each is active. This is the call/SMS inventory only — it does NOT report iMessage state. A Dial iMessage number can appear here as an ordinary voice/SMS-style row (often unassigned); that row says nothing about whether iMessage is provisioned or which agent owns it. For anything iMessage (blue bubble), use list_imessage_numbers instead. |
provision_phone_number | numbers:write | Write | phone_number, country_code | No contract flag | Buy and provision a phone number for the client (Twilio purchase + Retell import). Billable and capped per client (default 3 active). Pass a workflow_id to bind the number to that workflow (workflow-scoped); omit it for an account-wide client-scoped number. Fails with NUMBER_LIMIT_REACHED at the cap — release or deactivate one first. |
assign_number_to_workflow | numbers:write | Write | number_id | No contract flag | Reassign one of the client’s numbers to a workflow (workflow-scoped) or, with no workflow_id, make it the account-wide client-scoped number. Only reassigns within the client’s own workflows. |
set_number_sms | numbers:write | Write | number_id, enabled | No contract flag | Activate SMS per agent on an existing active Twilio number: enable inbound SMS on the number and set sms_workflow_id to bind it to that agent’s workflow. SMS is not provisioned separately — it rides on a number the account already has, and requires a Twilio-carrier number (Telnyx numbers cannot carry SMS). |
get_number_health | numbers:read | Read | number_id | No contract flag | Non-destructive health/read-back for one of the client’s numbers: reports the number’s active state, provider, its call binding and its SMS binding (enabled + owner workflow), and any issues — without placing a call or sending a message. |
deactivate_number | numbers:write | Write | number_id | No contract flag | Soft-deactivate one of the client’s numbers (stops it being used; does NOT release it from Twilio, so billing continues). Reversible. |
WhatsApp numbers (9)
WhatsApp numbers (9)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_whatsapp_numbers | whatsapp:read | Read | None | No contract flag | List the client’s WhatsApp numbers with connection status, quality rating, messaging-limit tier, account restrictions, last health-check time, connection type, and bound workflow. |
connect_whatsapp_number | whatsapp:write | Write | None | No contract flag | Start connecting a WhatsApp number for the client: returns a hosted Kapso setup link the client opens to finish onboarding (choose dedicated/coexistence, Meta login). IMPORTANT — give the returned setup URL to the user DIRECTLY as a clickable link (e.g. “Connect your WhatsApp number here: <url>”) and tell them to open it; the authorization happens entirely on that page and CANNOT be completed in chat, so never withhold the URL or claim you’ll connect it for them. Works even when a number is already connected — this adds a NEW one (several allowed per client). Once the user finishes on the hosted page, Nexor records the number and registers its inbound webhook + events automatically (no extra call needed); confirm with list_whatsapp_numbers. Capped per client (default 10) — fails with WHATSAPP_LIMIT_REACHED at the cap. Then bind it with assign_whatsapp_to_workflow. |
assign_whatsapp_to_workflow | whatsapp:write | Write | number_id | No contract flag | Bind one of the client’s WhatsApp numbers to a workflow (agent) — this is what makes inbound WhatsApp messages on that number reach the agent. Omit workflow_id to unbind. Makes the number the default for that workflow. |
deactivate_whatsapp_number | whatsapp:write | Write | number_id | No contract flag | Soft-deactivate one of the client’s WhatsApp numbers (stops it being used; reversible; does NOT disconnect it from Kapso). |
rename_whatsapp_number | whatsapp:write | Write | number_id, internal_name | No contract flag | Set the operator-facing internal label of one of the client’s WhatsApp numbers (does not change the WhatsApp display name shown to leads — for that use set_whatsapp_display_name). Pass an empty internal_name to clear it. |
reconcile_whatsapp_number | whatsapp:write | Write | number_id | No contract flag | Re-check a WhatsApp number against the provider and repair its route status — the fix for a number that shows a broken/invalid connection or after a failed setup return. Returns the refreshed state; run get_whatsapp_health afterwards to confirm. If the number needs a fresh Meta authorization, reconnect via connect_whatsapp_number (a new hosted setup link). |
get_whatsapp_business_profile | whatsapp:read | Read | number_id | No contract flag | Read the WhatsApp business profile shown to leads for one number: display name, its Meta review status (APPROVED / PENDING_REVIEW / etc.), and profile picture. Use before set_whatsapp_display_name to see the current name and whether a review is pending. |
set_whatsapp_display_name | whatsapp:write | Write | number_id, display_name | No contract flag | Submit a new WhatsApp display name (the name leads see) to Meta for review. Must be 3-256 characters. Meta review can take minutes to a couple of days; the response reports displayNameStatus=PENDING_REVIEW. Re-check with get_whatsapp_business_profile. |
set_whatsapp_profile_photo | whatsapp:write | Write | number_id, image_url | No contract flag | Replace the WhatsApp profile picture for a number from a public HTTPS image URL (Nexor ingests it into Meta). The image must already be hosted at an https:// URL — this tool does not upload local files. |
WhatsApp health (3)
WhatsApp health (3)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_whatsapp_health | whatsapp:read | Read | number_id | No contract flag | Live health and risk for one WhatsApp number. Returns quality_rating (GREEN/YELLOW/RED — RED means the account is at risk of Meta restriction), messaging_health (can_send_message AVAILABLE/LIMITED/BLOCKED with per-entity WABA/phone errors and suggested fixes), messaging_limit tier + daily ceiling, and route_verification (whether the provider route is valid or needs reconnect/resync). This is the tool for ‘is my WhatsApp at risk / why can’t it send / what’s my sending limit’. One call triggers several Meta calls, so results are briefly cached — don’t poll it in a tight loop. |
get_whatsapp_response_timing | whatsapp:read | Read | None | No contract flag | Read the client’s inbound WhatsApp debounce (buffer window): how long Nexor waits to group a lead’s rapid consecutive messages into a single AI reply. Returns buffer_window_seconds, whether it’s the default, and the allowed min/max. |
set_whatsapp_response_timing | whatsapp:write | Write | buffer_window_seconds | No contract flag | Set the client’s inbound WhatsApp debounce (buffer window) in seconds — larger groups more messages into one reply (more natural, slightly slower); smaller replies faster but may fragment. The change fans out to every active number’s webhook; the response reports per-number apply status. Typical range is 5-60 seconds. |
iMessage numbers (8)
iMessage numbers (8)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_imessage_offer | imessage:read | Read | None | No contract flag | Read the iMessage number add-on offer for the client BEFORE provisioning: the recurring monthly price (USD), the amount charged today (dueTodayUsd — the full first month, never prorated; 0 in a sandbox/test environment where no charge happens), whether the client can buy (canPurchase) and if not why (reason, e.g. no_subscription / trial_not_allowed / no_payment_method / external_billing / purchase_disabled), the card on file (brand + last4), how many iMessage numbers they already own, and a state of active | purchasable | unavailable. ALWAYS call this first and confirm the recurring charge with the user before calling provision_imessage_number. If state is “unavailable” the add-on is not enabled for this client — do not attempt to provision. |
list_imessage_numbers | imessage:read | Read | None | No contract flag | List the client’s iMessage numbers with their E.164 phone number, setupStatus (provisioning | ready | failed), setup error if any, the workflow (agent) each is bound to, display identity (displayFirstName, displayLastName, avatarUrl), and active state. imessageWorkflowId is the single OWNER; owner adds its name and agent group (masterWorkflowId, masterName) and sharedWith lists every sibling agent in that group that already sends and receives through this number without owning one — a number is shared across an agent group exactly like WhatsApp, so an agent listed in sharedWith is connected and needs no assign call. Use this to poll until a freshly provisioned number reaches setupStatus “ready”, and to find a number id for get_imessage_number, assign_imessage_to_workflow, set_imessage_display_name or set_imessage_profile_photo. |
provision_imessage_number | imessage:write | Write | accept_charge | accept_charge: true | Buy and provision a NEW iMessage number for the client. THIS CHARGES MONEY: it charges the FULL first month today (never prorated) and adds a recurring monthly line (see get_imessage_offer for the exact price) to the client’s Stripe subscription. You MUST first call get_imessage_offer, show the user the recurring price and the amount charged today, and get their explicit confirmation — only then call this with accept_charge:true. Fails with imessage_consent_required if accept_charge is not true, and with an eligibility code (e.g. imessage_no_subscription, imessage_no_payment_method, imessage_purchase_disabled) when the client cannot be charged. The charge is idempotent per call. On success the number provisions asynchronously — poll list_imessage_numbers until setupStatus is “ready”, then bind it with assign_imessage_to_workflow. |
assign_imessage_to_workflow | imessage:write | Write | number_id | No contract flag | Bind one of the client’s iMessage numbers to a workflow (agent) — this is what makes inbound iMessages on that number reach the agent. The number is then shared by the agent’s whole agent group (master_workflow_id): every sibling sends and receives through it automatically (new inbound leads land on the owner agent; existing leads follow their active run), so bind ONCE per group and never re-assign it to a sibling that list_imessage_numbers already shows in sharedWith. Unless a display identity was set by hand (set_imessage_display_name), the number’s display name follows the owner agent’s persona. Omit workflow_id to unbind (the number stays owned/billed, just detached). A group (or a standalone agent) keeps at most one iMessage number, so binding a number to an agent whose group already has one replaces it. |
get_imessage_number | imessage:read | Read | number_id | No contract flag | Read one iMessage number’s live details: E.164 phone number, setupStatus (provisioning | ready | failed) and setup error, the workflow (agent) it is bound to plus its agent group (owner) and the sibling agents sharing it (sharedWith), and the blue-bubble display identity leads see — firstName, lastName, avatarUrl — plus active state. Use before set_imessage_display_name / set_imessage_profile_photo to see the current identity. Fails with imessage_number_not_found for a number outside this client. |
reconcile_imessage_number | imessage:write | Write | number_id | No contract flag | Re-check one iMessage number against Dial (the provider) and repair its local setupStatus — the fix for a number stuck on “provisioning” or “failed” in list_imessage_numbers even though it is actually ready. list_imessage_numbers reads a locally cached status that is normally advanced only by Dial’s setup webhook, so a single missed webhook leaves the number permanently desynced; get_imessage_number reads the live truth from Dial but does not persist it. This tool fetches the live Dial status and writes it back, so the list/capabilities path matches reality. Returns the refreshed number; re-run list_imessage_numbers or get_imessage_number afterwards to confirm. Fails with imessage_number_not_found for a number outside this client. |
set_imessage_display_name | imessage:write | Write | number_id | No contract flag | Set the first and/or last name shown beside an iMessage number’s messages in recipients’ Messages apps (max 30 characters each; pass an empty string to clear one). Propagates to devices within minutes. Once set by hand the identity is authoritative: it no longer follows the agent persona when the number is (re)assigned. Fails with imessage_number_invalid_fields (+ fieldErrors) when a value is rejected. Read the current identity first with get_imessage_number. |
set_imessage_profile_photo | imessage:write | Write | number_id, image_url | No contract flag | Replace the profile photo shown beside an iMessage number’s messages from a public http(s) image URL (jpeg/png/gif/webp, ≤5 MB; the platform mirrors it and returns its stable avatarUrl). Photos can be replaced but NOT removed. This tool does not upload local files — the image must already be hosted. Propagates to devices within minutes. |
Email senders (8)
Email senders (8)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_email_senders | senders:read | Read | None | No contract flag | List the client’s registered email senders (domains and individual mailboxes) with their verification status and whether each can send. |
register_email_domain | senders:write | Write | sender_email, sender_name | No contract flag | Register a sender under a domain the client owns (e.g. sales@acme.com), for sending at scale. Returns the DNS records (SPF/DKIM) the client must add to that domain — adding them is a human step outside this tool. Once added, call verify_email_domain, or poll check_email_sender_status. |
register_email_sender | senders:write | Write | sender_email, sender_name | No contract flag | Register a personal mailbox (e.g. Gmail/Outlook) as a sender via a magic-link. Nexor emails a verification link to that mailbox — the mailbox owner must open it and click to confirm; that click is a human step outside this tool. Poll check_email_sender_status afterwards to see when it completes. |
check_email_sender_status | senders:read | Read | sender_email | No contract flag | Check whether a sender’s verification (DNS records or magic-link click) has completed and it can now send. Verification itself happens outside this tool, by a human — use this to poll the result after register_email_domain or register_email_sender. |
verify_email_domain | senders:write | Write | domain | No contract flag | Trigger a DNS verification check for a domain sender, AFTER the client has already added the DNS records returned by register_email_domain. This tool only checks — adding the records is a human step outside this tool. |
get_email_domain_dns_records | senders:read | Read | domain | No contract flag | Re-fetch the DNS records (SPF/DKIM) for a pending domain sender — e.g. if the client lost the ones returned by register_email_domain. Only reads; adding the records to the domain is a human step outside this tool. |
resend_email_verification | senders:write | Write | sender_email | No contract flag | Resend the magic-link verification email for a pending sender mailbox. |
delete_email_sender | senders:write | Write | sender_id | No contract flag | Remove a registered sender. |
Template pools (8)
Template pools (8)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_template_pool | whatsapp:read | Read | workflow_id | No contract flag | Read a workflow’s WhatsApp template pool state in one call: the active rotation pool (name, weight, category), the opening-template config (mode auto|only|subset + names), and what’s available to add (approved openers and all templates with status + internal_type). Use it before set_template_pool / set_opening_templates to see current state. |
set_template_pool | whatsapp:write | Write | workflow_id, templates | No contract flag | Replace a workflow’s WhatsApp cadence template pool with EXACTLY the given list — full-state replace, so a single call covers adding AND removing (retiring) templates atomically: anything omitted is removed from the pool. Each entry: { name, weight? (0.01-10, default 1) } and must be an APPROVED template on the client’s WhatsApp business account. Read the current pool with get_template_pool first, then send the complete desired list. |
set_opening_templates | whatsapp:write | Write | workflow_id, mode | No contract flag | Configure which WhatsApp templates a workflow may use as the OPENING (first-contact) message. mode ‘auto’ lets the cadence pick any eligible opener; ‘only’ locks it to exactly one template (template_names must have exactly 1 entry); ‘subset’ restricts it to the given names (at least 1). Names must be APPROVED templates with internal_type greeting, opening, legacy_greeting or outbound. Fails with WOULD_EMPTY_OPENING if the change would leave the workflow with no valid opener. |
sync_whatsapp_templates | whatsapp:write | Write | None | No contract flag | Pull the latest template state from Meta for all of the client’s WhatsApp business accounts: imports new templates, refreshes statuses (PENDING -> APPROVED/REJECTED) and quality scores. Returns { synced, created, updated, channels, warnings }. Run it when a template you expect is missing from list_whatsapp_templates or its status looks stale. |
check_whatsapp_template_approvals | whatsapp:write | Write | None | No contract flag | Terminal-friendly approval inspection. By default first syncs every connected WhatsApp business account from Meta, then returns ALL templates in one response with status, quality_score, rejection_reason, timestamps, and totals.by_status. Set refresh=false only when you intentionally want the cached local state. Use after batch submission until each template is APPROVED or REJECTED; rejected drafts can be revised through review_whatsapp_template_drafts under a new name. |
get_whatsapp_template_approval_status | whatsapp:read | Read | None | No contract flag | THE tool for “have my templates been approved?” / “how long until they’re approved?” — in Nexor a bare “template” ALWAYS means a WhatsApp template, so route those questions here. Refreshes from Meta by default, then returns a decision-ready summary — NOT a raw dump: counts { total, approved, pending, rejected, … }, all_approved / none flags, each PENDING template with how long it has actually been pending (pending_hours, so you answer with real data instead of a guessed ETA), longest_pending_hours, some_pending_over_24h, each REJECTED template with its Meta reason, and review_window { typical, max_hours }. Also returns numbers[] — ONLY the verified, fully-connected WhatsApp number(s) (a number still finishing setup is excluded), each with phone_number and whether it is bound to an agent. Templates only exist for a fully connected WhatsApp number: if none is connected the response is { no_whatsapp: true, numbers: [] } — then tell the customer to connect a WhatsApp number first (connect_whatsapp_number), do NOT say they have zero templates. Otherwise answer with the counts + the canonical window (approval is asynchronous and Meta-owned: usually minutes to ~1 hour, occasionally up to 24-48h) — never invent a precise time. If some_pending_over_24h is true, say it is taking longer than usual. For the full raw list use list_whatsapp_templates / check_whatsapp_template_approvals; for one template use get_whatsapp_template. Set refresh=false to skip the Meta sync and read cached state. |
set_whatsapp_template_purpose | whatsapp:write | Write | template_id, internal_type | No contract flag | Assign the cadence purpose of an existing or Meta-synced WhatsApp template. This is how an already-connected account can make a pre-existing template eligible for opening, follow-up, reminder, or reactivation behavior without recreating it. The template content and Meta approval status are unchanged. Use get_whatsapp_template first; only APPROVED greeting templates can subsequently be selected by set_opening_templates. |
delete_whatsapp_template | whatsapp:write | Write | template_id, confirm | confirm: true | Soft-delete a WhatsApp template (tenant-scoped; best-effort delete on Meta too). Preview-then-confirm flow: call with confirm=false first — it returns { requires_confirm: true, template: { name, status, internal_type, is_in_pool } } and writes NOTHING, so you can show the user what would be removed (especially whether it is still in a cadence pool). Only after reviewing that preview, call again with confirm=true to actually delete; the response reports removed_from_pools and meta_deleted. |
Analysis (3)
Analysis (3)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_analysis_snapshot | reports:read | Read | None | No contract flag | Precomputed conversation-quality snapshot for the client (or one workflow): eval summary (scored conversations, average score, per-dimension averages, failure buckets), accuracy signals, seeker findings, the worst conversations (with reasons, transcript excerpts for the top 3) and system context. Fast — use it FIRST for “how is the agent doing” questions; only use run_deep_analysis when you need a fresh investigation of a specific question. |
list_analysis_findings | reports:read | Read | None | No contract flag | List the analysis engine’s stored findings/corrections/preferences for the client: title, summary, class, status, client impact, confidence and recommendation. Defaults to status ‘open’ (actionable backlog); pass status ‘all’ or a specific status to see history. Findings are produced by prior analysis runs — use run_deep_analysis to generate new ones. |
run_deep_analysis | reports:read | Read | question | No contract flag | Run a fresh deep analysis of recent conversations to answer a specific question (e.g. “why are leads ghosting after the price question”). LONG-RUNNING: this call is synchronous and may take 1-3 minutes — tell the user it is in progress and do not retry while waiting. Expensive and rate-limited to 5 runs per day per API key; when capped it returns { rate_limited: true } with the most recent run’s findings instead. For a quick health overview prefer get_analysis_snapshot. |
Lead diagnostics (7)
Lead diagnostics (7)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_lead_turns | leads:read | Read | lead_id | No contract flag | List a lead’s agent turns (one turn = the agent processing one message): trace id, timestamp, channel, model, message/response previews, tools used, errors, latency and token usage. Two modes: (1) paging — use limit + next_before_ts to walk back in time; (2) search — pass message_text, provider_message_id, direction or around_ts to locate WHICH turn handled a specific message (the response includes match.trace_id with a confidence label). Drill into a turn with get_lead_turn. |
get_lead_turn | leads:read | Read | lead_id, trace_id | No contract flag | Full detail of one agent turn (by trace_id from list_lead_turns): metadata (model, tokens, duration, errors), the lead message + final response + agent reasoning, whether internal content leaked, tools used, the final gate decision, LLM outputs and a step-by-step timeline. Secrets are redacted and URLs reduced to host-only. This is THE tool for “why did the agent say that” questions. |
get_lead_run_state | leads:read | Read | lead_id | No contract flag | A lead’s current automation state in one call: the active workflow run (status, pause/human-takeover/escalation/dispatch state, next action time), the agent’s memory (summary + recent interaction log) and the collected field values with confidence + evidence quotes. Use include to fetch only some sections. This is the first stop for “why isn’t the agent messaging this lead” questions. |
get_lead_verdicts | leads:read | Read | lead_id | No contract flag | Quality-judge verdicts and alert events for a lead’s conversations: per-turn judge results (pass/fail, score, severity, reason) plus raised alerts (ai / workflow-config / whatsapp categories, redacted previews). Filter by trace_id or run_id to inspect one turn/run, or by a time window. Pairs with get_lead_turn to see WHAT the agent did vs how it was judged. |
get_lead_call | leads:read | Read | lead_id, call_id | No contract flag | Full detail of one call with a lead (call id from get_lead_history, which lists the calls): status, duration, success flag, sentiment, disconnection reason, summary, transcript and the AI call analysis. |
list_offers | workflows:read | Read | None | No contract flag | The client’s offer catalog (products/services the agent can pitch): name, description, category, status, source and top items. Pass workflow_id to see only the offers linked to that workflow (an empty result means the workflow has none linked — there is no fallback to the full catalog). Pass offer_id for one offer’s detail including its items. |
get_client_profile | workflows:read | Read | None | No contract flag | The authenticated client’s business profile as the agent sees it: name, industry, company size, country (+ secondary countries), timezone, language, website, primary communication channel, lifecycle stage, notes and which settings keys are configured. Useful context before diagnosing agent behavior or drafting client-facing copy. |
Documentation (1)
Documentation (1)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
search_docs | Authenticated key | Read | None | No contract flag | Search Nexor’s product documentation (bundled advanced-feature guides + the live docs site) or read one doc. Pass EXACTLY ONE of: q (search — returns up to 6 results with title, description and path) or path (read — returns the doc content, truncated at ~9000 chars with a marker). Typical flow: search with q, then fetch the best result’s path. Public docs only — no tenant data. |
Pipeline (2)
Pipeline (2)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
reorder_workflow_statuses | workflows:write | Write | workflow_id, ordered_status_keys | No contract flag | Replace the order of every mutable status after the protected Lead created, Contacted, and Engaged stages. First call get_workflow, then pass every remaining persisted status key exactly once in ordered_status_keys; omission, duplication, an unknown key, or a changed pipeline fails without mutation. The backend keeps the protected head at sort_order 0–2 and assigns the submitted tail contiguously from 3. Read back with get_workflow and compare the exact order before reporting success. Do not include synthetic Discarded stages. |
get_pipeline_impact | reports:read | Read | workflow_id | No contract flag | Impact preview of a workflow’s pipeline BEFORE a destructive change (deleting statuses, moving leads, deactivating the workflow): total board-visible leads bucketed by run state — early, active, settled (terminal/inactive collapsed), ai_paused and awaiting_human — plus the top statuses by count. Use it to tell the user “this affects N active leads” and get confirmation. For per-status funnel metrics use get_funnel_report instead. |
Knowledge bases (9)
Knowledge bases (9)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_knowledge_bases | workflows:read | Read | None | No contract flag | List the client’s knowledge bases with status and document/size counts. Pass workflow_id to see only the KBs attached to that workflow, ordered by retrieval priority (each row then includes its priority). Ideal for “what knowledge bases exist”, “which KBs feed this workflow’s agent”, or finding a KB id before attaching or adding documents. |
create_knowledge_base | workflows:write | Write | name | No contract flag | Create a new, empty knowledge base for the client. After creating, add content with add_kb_document and make it live for an agent with attach_knowledge_base. Ideal for setting up a fresh document collection (e.g. “Pricing FAQ”, “Product docs”). |
update_knowledge_base | workflows:write | Write | knowledge_base_id | No contract flag | Edit a knowledge base’s own fields: its name, its description (what it contains / when the agent should use it), and its status (‘archived’ shelves it so agents stop using it without deleting; ‘active’ restores it). Provide only the fields you want to change; at least one is required. This is the ONLY way to change a KB’s description or rename a KB, there is no dashboard-only field. Returns 404 if the KB does not belong to this client. Ideal for “rename this KB”, “set its description to …”, or “archive the old pricing KB”. |
attach_knowledge_base | workflows:write | Write | workflow_id, knowledge_base_id | No contract flag | Attach an existing knowledge base to a workflow so that workflow’s agent can retrieve from it. priority orders retrieval when several KBs are attached (lower = consulted first); omitted, it appends after the current lowest-priority KB. Returns 409 CONFLICT if already attached, 404 if the KB does not belong to this client. Ideal for “make the pricing KB available to the sales workflow”. |
detach_knowledge_base | workflows:write | Write | workflow_id, knowledge_base_id | No contract flag | Detach a knowledge base from a workflow. The KB and its documents are NOT deleted — the workflow’s agent just stops retrieving from it. Returns 404 if the KB was not attached to that workflow. Ideal for “stop the sales agent from using the old pricing KB”. |
list_kb_documents | workflows:read | Read | knowledge_base_id | No contract flag | Page through the documents inside one knowledge base: name, source type (file/web/text), processing status, chunk count and timestamps. Ideal for “what’s in this KB”, checking whether a just-added document finished processing (status ready), or finding a document id to rename/delete. |
add_kb_document | workflows:write | Write | knowledge_base_id, name, text | No contract flag | Add a text document to a knowledge base. The text (max 200KB) is chunked and embedded asynchronously — the response is { document_id, status: “pending” }; poll list_kb_documents until status is “ready”. Ideal for ingesting FAQs, policy text, product descriptions or any copy the agent should be able to cite. |
rename_kb_document | workflows:write | Write | knowledge_base_id, document_id | No contract flag | Rename a document and/or replace its text content. Pass name to rename (embeddings untouched), text to replace the content, or both. Changing content re-indexes the document: it is re-chunked and re-embedded asynchronously, status goes back to ‘pending’ and returns to ‘ready’ when done (poll list_kb_documents). Only text documents (source type ‘text’) accept new content: file/web documents return 409. Returns 404 if the document is not in that KB. Ideal for tidying names after bulk ingestion or updating a FAQ/pricing doc in place. |
delete_kb_document | workflows:write | Write | knowledge_base_id, document_id | No contract flag | Permanently delete a document (and its chunks/embeddings) from a knowledge base. The agent stops retrieving it immediately. Returns 404 if the document is not in that KB. Ideal for removing outdated or wrong content the agent keeps citing. |
Webhooks (7)
Webhooks (7)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_webhooks | workflows:read | Read | None | No contract flag | List the client’s outbound webhooks: name, target URL, subscribed events, active flag and delivery health. Secrets are never included. Ideal for “what webhooks are configured”, “is anything listening to meeting.completed”, or finding a webhook id to update/test/delete. |
configure_status_webhook | workflows:write | Write | workflow_id, status_key, name, url | No contract flag | Create an outbound webhook that fires only when a lead enters one specific status in one specific workflow/agent. It subscribes to workflow_run.status_changed and installs exact workflow_id + to_status.key filters, plus any extra filters. The signing_secret is returned EXACTLY ONCE. Use one webhook per destination/behavior when different statuses need different payloads or receivers. |
get_webhook | workflows:read | Read | webhook_id | No contract flag | Get one webhook’s full configuration: events, filters, auth type, retry setting and payload template. auth_config is redacted to key names and the signing_secret is never returned (it is only shown once, at create time). Ideal for auditing exactly what a webhook sends and when. |
create_webhook | workflows:write | Write | name, url | No contract flag | Create an outbound webhook. url must be https and publicly reachable (private/metadata IP ranges are rejected server-side). The response includes the signing_secret EXACTLY ONCE — surface it to the user immediately; it cannot be retrieved later. Ideal for “notify my CRM when a meeting completes” style integrations. |
update_webhook | workflows:write | Write | webhook_id | No contract flag | Update an existing webhook. Only the fields you pass change; use is_active to pause (false) or resume (true) deliveries without deleting the webhook. Returns the updated, redacted configuration — the signing_secret is never re-issued. Ideal for pointing a webhook at a new URL, changing its event list, or toggling it. |
delete_webhook | workflows:write | Write | webhook_id | No contract flag | Permanently delete a webhook; deliveries stop immediately. Returns 404 if no webhook matched. To pause temporarily instead, use update_webhook with is_active:false. |
test_webhook | workflows:write | Write | webhook_id | No contract flag | Fire a synthetic test event at a webhook’s URL and report the outcome: { delivered, response_status, duration_ms, error }. The remote response body is never reflected back. Ideal for verifying a newly created or just-edited webhook actually receives events before relying on it. |
Tags (3)
Tags (3)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_tags | leads:read | Read | None | No contract flag | Page through the client’s tag catalog (id, name, slug, description). search matches name, slug and description. Ideal for “what tags exist”, finding a tag id before set_tag/delete_tag, or checking a slug before creating a similar tag. To tag an individual lead use sync_lead_tags instead. |
set_tag | leads:write | Write | None | No contract flag | Create or update a tag in the catalog (upsert). OMIT tag_id to CREATE: name is required and slug is auto-generated from it unless given. PASS tag_id to UPDATE that existing tag’s name/slug/description (only provided fields change). Returns the tag; 409 CONFLICT if the slug already exists for this client. Leads already carrying an updated tag keep it — renaming is safe. |
delete_tag | leads:write | Write | tag_id | No contract flag | Permanently delete a tag from the catalog AND remove it from every lead that carries it (cascade). Returns 404 if no tag matched. Ideal for retiring obsolete tags; to merely rename one, use set_tag with tag_id instead. |
Processors (6)
Processors (6)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_processors | workflows:read | Read | None | No contract flag | List the client’s pre-workflow processors (lead-routing rule engines): name, default workflow, active flag and rule count. Deactivated processors are hidden unless include_inactive is true. Ideal for “how are incoming leads routed”, or finding a processor id before get_processor / set_processor_rule. |
get_processor | workflows:read | Read | processor_id | No contract flag | Get one processor with its full rule list: each rule’s conditions, target workflow, evaluation position and active flag. Rules are evaluated in position order (1 first); the first match wins. Ideal for auditing routing logic before editing it with set_processor_rule. |
set_processor | workflows:write | Write | None | No contract flag | Create or update a pre-workflow processor (upsert). OMIT processor_id to CREATE: name and default_workflow_id are required, and you may seed up to 50 rules atomically (if any rule fails, the whole create rolls back). PASS processor_id to UPDATE name/description/default_workflow_id/is_active — rules cannot be passed on update; manage them with set_processor_rule / delete_processor_rule. default_workflow_id must belong to this client. |
delete_processor | workflows:write | Write | processor_id | No contract flag | Soft-deactivate a processor: it stops evaluating rules and routing leads, but the processor and its rules are kept (visible via list_processors include_inactive:true) and can be reactivated with set_processor is_active:true. Returns { deactivated: true }. |
set_processor_rule | workflows:write | Write | processor_id | No contract flag | Create or update one routing rule on a processor (upsert). OMIT rule_id to CREATE: name, workflow_id and conditions (1-20, ALL must match) are required. PASS rule_id to UPDATE — only provided fields change; passing position (1-based, 1 = evaluated first) also reorders the rule, which is how rules are reordered. workflow_id must belong to this client. Empty condition lists are rejected: they would match every lead. |
delete_processor_rule | workflows:write | Write | processor_id, rule_id | No contract flag | Permanently delete one routing rule from a processor. Leads that would have matched it fall through to later rules or the default workflow. Returns 404 if the rule is not on that processor. |
Meeting types (5)
Meeting types (5)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_meeting_types | workflows:read | Read | workflow_id | No contract flag | List a workflow’s meeting types exactly as the dashboard form shows them: name, duration, meeting buffer, min notice, max days ahead, calendar title/invite templates, type (video with video_provider google_meet/teams, phone, in_person with location_details), overbooking + maximum per time slot, color, is_active, the free-text agent_selection_criteria and the enforced agent_selection_rules (see set_meeting_type_routing). Ideal for “what appointment kinds can this workflow book” or finding a meeting type id before update_meeting_type / delete_meeting_type. |
create_meeting_type | workflows:write | Write | workflow_id, name | No contract flag | Create the first and only meeting type for an appointment workflow that currently has none. Requires a name; omitted fields receive the dashboard defaults (30 min, 15 min buffer, 2 h notice, 14 days ahead, video). The type is created ACTIVE; it becomes bookable once a host with availability is assigned (add_executive). If one already exists, returns it explicitly with status=“existing” and never creates another. If several exist, fails with MULTIPLE_MEETING_TYPES so the operator can consolidate them. Bad values fail with 400 listing the allowed options (e.g. location_type must be video, phone or in_person). |
update_meeting_type | workflows:write | Write | workflow_id, meeting_type_id | No contract flag | Update one meeting type by id (at least one field required; only provided fields change) — every field of the dashboard meeting form. Pass null for description / event_description_template / location_details / video_provider / max_bookings_per_slot to clear them. is_active switches the type on or off: activating while another type is active fails with 409 MT_EXISTS; deactivating the only active type of a live appointment agent fails with 409 LAST_ACTIVE_MEETING_TYPE. Bad values fail with 400 listing the allowed options. Returns 404 if the meeting type is not in that workflow. Ideal for “make demos 45 minutes”, “require 24h notice”, “make it in person at the showroom”, “use Teams instead of Meet”. |
set_meeting_type_routing | workflows:write | Write | workflow_id, agent_selection_rules | No contract flag | Write the enforced host routing rules (agent_selection_rules) of a meeting type and read them back. Unlike agent_selection_criteria (free text the AI interprets), these rules are applied server-side when slots are offered and booked. Every host reference is validated against the hosts assigned to the meeting type (list_executives; pass user ids, names are resolved to the assigned host and stored by name). Examples: two hosts strictly alternating → {routing:“cycle”, tiers:[{agents:[A,B], mode:“cycle”, pattern:[0,1]}]}; leads with renta >= 50000 to Ana, the rest split 2:1 between Bruno and Carla, below 0 disqualified → {field:“renta”, tiers:[{min:50000, agents:[ana]}, {min:0, agents:[bruno, carla], weights:[2,1]}, {min:-1e9, action:“disqualify”}]}. Pass agent_selection_rules: null to clear. meeting_type_id may be omitted when the workflow has exactly one meeting type. 400 lists what is wrong (unassigned host, unordered tiers, unknown mode). |
delete_meeting_type | workflows:write | Write | workflow_id, meeting_type_id | No contract flag | Delete a meeting type from a workflow. The response reports impact: agent_assignments_removed and future_events_orphaned (already-booked meetings keep their calendar events but lose the type link). Returns 409 LAST_APPOINTMENT_TYPE when the workflow’s goal is ‘appointment’ and this is its only remaining type — an appointment workflow must keep at least one. 404 if the id is not in that workflow. |
Agent configuration (12)
Agent configuration (12)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_workflow_status | workflows:read | Read | workflow_id, status_key | No contract flag | Read one workflow status’s complete configuration without changing it: label/category/flags, entry_hint, required-field and transition gates, timeout, transfer, pause and assignment behavior. Use this before update_workflow_status and preserve exact labels/casing unless the user explicitly asks to rename them. |
update_workflow_status | workflows:write | Write | workflow_id, status_key | No contract flag | Configure one existing funnel status exactly like the dashboard stage editor. entry_hint is the natural-language qualification/entry gate for that status; requires_all_fields or required_field_keys adds a deterministic variable gate; transition_rules adds structured qualification rules; timeout_config auto-moves stale leads; terminal transfer_config moves the lead to another specialized agent/workflow and can copy collected fields; pause_bot stops the current agent; assignment_config ({mode, agent_ids, restrict_booking_to_assignee}) assigns a human team member on entry — only ONE status per workflow may carry it (409 ASSIGNMENT_CONFIG_EXISTS with holder_status_key; clear it there with assignment_config:null first). category must be active/won/lost/paused/deferred. System stages other than the escalation stage and the virtual Discarded stage refuse writes with 409 PROTECTED_STATUS. Read get_workflow_status first and change only intended fields; preserve the exact label unless renaming was explicitly requested. |
list_workflow_voices | workflows:read | Read | workflow_id | No contract flag | List the voice catalog usable by a workflow’s call agent: public voices plus the client’s private voices, each with id, voice_id, name, provider, language, gender and a preview_url (a playable .mp3 sample link you can hand the user to hear the voice). Use this to pick a voice_id for set_workflow_voice, or to show the user samples before switching. |
set_workflow_voice | workflows:write | Write | workflow_id | No contract flag | Switch and/or tune the workflow’s call voice. To CHANGE which voice the lead hears, pass ONE of: gender (‘male’/‘female’ — the server picks a matching voice in the workflow’s language, e.g. a male voice for ‘voz de hombre’), voice_id (an exact id from list_workflow_voices), or voice_name (a name to match). The server writes the effective voice where the call runtime actually reads it and returns resolved_voice with a preview_url (.mp3) you should share with the user so they can hear it. To TUNE the current voice, pass a voice object with knobs (voiceSpeed, voiceTemperature, volume); these are key-level merged and preserved. Provide at least one of gender/voice_id/voice_name/voice. Unknown voice_id or no gender match returns INVALID_VOICE with available_voices. Version-snapshotted. |
get_workflow_cadence | workflows:read | Read | workflow_id | No contract flag | Read a workflow’s outreach cadence (block-config): the sequenced contact blocks (channel, delay, templates), timing/business-hours config and retry behaviour. |
set_workflow_cadence | workflows:write | Write | workflow_id, block_config | No contract flag | Replace a workflow’s outreach cadence (block-config). Read the current one with get_workflow_cadence first and send the full edited object — this is a whole-document PUT, not a merge. It does NOT set the WhatsApp opening template: dayConfig.config.opening_template is ignored when unchanged and REJECTED when changed (use set_opening_templates, which validates the opener). WhatsApp template names referenced by blocks must exist and be APPROVED on the client’s WABAs; business hours are re-derived server-side. |
get_workflow_qualification | workflows:read | Read | workflow_id | No contract flag | Read a workflow’s lead-qualification model (futurology): the scoring categories and the conditions that classify a lead into them. |
set_workflow_qualification | workflows:write | Write | workflow_id | No contract flag | Write a workflow’s lead-qualification model (futurology). categories and conditions address the SAME rows under two vocabularies (category_key/category_name/description = condition_key/condition_name/condition_description), so a key may appear once per call. Every row needs its key AND its human name (condition_name / category_name — never invented by the server); threshold_type, timing_type and scope are closed enums and wrong types are refused with 400 naming the index and field. Default is a merge (upsert by key, unsent columns keep their stored value); pass replace:true to clear the whole model and write what you send. get_workflow_qualification returns rows under the same field names, so a read-modify-write needs no translation. Provide at least one of categories/conditions. |
delete_workflow_status | workflows:write | Write | workflow_id, status_key | No contract flag | DESTRUCTIVE: permanently delete a funnel status from a workflow. Call get_pipeline_impact FIRST to see how many leads sit on each status. If any leads are on the status you must pass migration_target (the status key those leads are moved to) or the call fails with STATUS_HAS_LEADS and the lead count. The only initial status and the last terminal status can never be deleted. |
delete_workflow_field | workflows:write | Write | workflow_id, field_key | No contract flag | DESTRUCTIVE: permanently delete a data field (variable) from a workflow. Leads’ previously collected answers are preserved (detached from the field but kept in history); the field is removed from the funnel and the agent stops collecting it. Refuses with FIELD_IN_USE (409) when the field still gates a status (required fields / transition rules) or a qualification rule — remove those references first, then delete. There is no undo — prefer editing the field with update_workflow_structure over deleting it. |
delete_workflow | workflows:write | Write | workflow_id | confirm: true | DESTRUCTIVE / IRREVERSIBLE: delete an entire agent (workflow) — its funnel, fields, cadence and configuration. It is a soft-delete (archive), not a hard purge, but there is no in-chat undo, so treat it as permanent and get explicit user sign-off first. Preview-then-confirm: call with confirm:false (or omit it) to get a { status:‘needs_confirmation’ } preview that writes NOTHING; only call again with confirm:true to actually delete. The backend REFUSES the delete when the agent still has active leads in its pipeline: that returns { status:‘blocked’, reason:‘agent_has_leads’, lead_count } (no exception) so you can tell the user how many leads must be moved or closed first (use get_pipeline_impact to inspect them). / DESTRUCTIVO E IRREVERSIBLE: elimina un agente (workflow) completo. Es un archivado (borrado suave) sin deshacer en el chat; pide confirmación explícita. Primero confirm:false para previsualizar sin escribir, luego confirm:true para eliminar. Si el agente aún tiene leads activos, el backend lo rechaza y devuelve { status:‘blocked’, reason:‘agent_has_leads’, lead_count } sin lanzar error. |
restore_workflow | workflows:write | Write | workflow_id | No contract flag | Un-archive (restore) an agent that was previously deleted with delete_workflow. Archive is a soft-delete, and this is its inverse: it clears the archive so the agent is listable and editable again. The agent comes back PAUSED and inactive — the channels it had (WhatsApp / phone / Instagram / Meta) were released when it was archived and are NOT re-attached, and its old leads/runs stay paused; reconnect channels and activate explicitly afterwards. Find the archived agent’s id first with list_workflows include_archived:true. Idempotent: restoring an agent that isn’t archived returns { status:‘already_live’ } without error. / Restaura (desarchiva) un agente eliminado con delete_workflow. El borrado es suave y esto es su inverso: el agente vuelve a aparecer y a ser editable, pero PAUSADO e inactivo; sus canales fueron liberados al archivar y NO se reconectan solos. Usa list_workflows include_archived:true para encontrar su id. |
Executives (4)
Executives (4)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_executives | workflows:read | Read | workflow_id | No contract flag | List a workflow’s executives — the human hosts who take its meetings — with their user, active flag and assigned meeting types. |
add_executive | workflows:write | Write | workflow_id, user_id | No contract flag | Add a team member as an executive (meeting host) of a workflow, optionally restricted to specific meeting types. The user must belong to the client’s team (see get_team); adding someone who is already a host fails with a conflict. Assigning the host to a meeting type that was created inactive (seeded in the dashboard before any host existed) activates it, unless another type is already active; the response lists meeting_types_activated. Pass meeting_type_ids explicitly for that to happen — omitting it links no meeting type. |
update_executive | workflows:write | Write | workflow_id, executive_id | No contract flag | Update a workflow executive: toggle is_active and/or replace their full meeting-type set (meeting_type_ids overwrites the current assignments — pass the complete list). Provide at least one field. Linking the host (or switching them back on) activates a meeting type that was created inactive, unless another type is already active; the response lists meeting_types_activated. |
remove_executive | workflows:write | Write | workflow_id, executive_id | No contract flag | Remove an executive (meeting host) from a workflow. Their existing meetings are untouched; they just stop receiving new ones. |
Advanced (1)
Advanced (1)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
describe_advanced_features | workflows:read | Read | None | No contract flag | Map every surface in the dashboard Advanced hub to its purpose, execution model, MCP tools, and safety considerations. Use this as the entry point for Advanced configuration. |
Agent system (2)
Agent system (2)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
describe_agent_configuration | workflows:read | Read | None | No contract flag | Complete reference for designing and configuring one or many Nexor agents through MCP: prompts, statuses and qualification gates, variables, cadence, tools, pre-execution hooks, stage gates, webhooks, transfers, Cloud/Scheduled Functions, and the required clarification/sign-off workflow. Call this before proposing a non-trivial or multi-agent build. |
review_agent_system_plan | workflows:read | Read | plan | No contract flag | Preflight a complete single- or multi-agent configuration before any mutations. Finds missing identities, goals, status gates, transfer targets, channels/language/timezone (every agent needs an IANA timezone — it anchors booking slots), planned channels with no verified resource on the account, webhook destinations, booking prerequisites for appointment agents (meeting type, host, calendar plan or external provider), and operational risks; returns a stable plan fingerprint and the exact final sign-off prompt. Do not create/update agents, tools, hooks, webhooks, functions, schedules, jobs, or transfers until this review is clean and the user explicitly approves the fingerprint. |
Agent groups (4)
Agent groups (4)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
list_agent_groups | workflows:read | Read | None | No contract flag | List active agent groups with their ids, names, and descriptions. Use this before assigning an agent by group name; list_workflows returns each agent’s current master_workflow_id membership. |
create_agent_group | workflows:write | Write | name | No contract flag | Create an agent group. To create and assign in one conversation, create the group first, then call update_workflow with the returned group id as master_workflow_id, and read back both lists. |
update_agent_group | workflows:write | Write | agent_group_id | No contract flag | Rename an agent group or change its description. Pass only the fields to update. This does not change agent membership; use update_workflow.master_workflow_id for that. |
delete_agent_group | workflows:write | Write | agent_group_id | Operator approval in instructions | Delete (deactivate) an agent group after explicit confirmation. The backend unassigns its member agents; it does not delete those agents. Read back list_agent_groups and list_workflows after the write. |
Calendars (3)
Calendars (3)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
connect_calendar | calendars:write | Write | user_id, provider | confirm: true | Start connecting a team member’s Google or Outlook calendar (the host’s busy time and conferencing link for bookings). Returns connect_url, expires_at (10 min) and connection_request_id. IMPORTANT — hand connect_url to the user DIRECTLY as a clickable link and say who must open it (the host signs in and grants calendar access there; it cannot be completed in chat). When the target user is NOT the operator you are talking to, you MUST first tell the operator whose calendar will be linked and get an explicit yes, then call again with confirm=true — the backend refuses with CONFLICT/CONFIRMATION_REQUIRED otherwise. Other refusals: USER_NOT_ACCEPTED (fix: invite_user and wait for acceptance), PROVIDER_UNAVAILABLE (Outlook not enabled here), OTHER_PROVIDER_ACTIVE (disconnect the other provider first). Afterwards poll get_calendar_connections until status=active and scopes_ok=true. |
get_calendar_connections | calendars:read | Read | None | No contract flag | List calendar connections (Google, Outlook, Calendly) for the account or one team member: provider, status (active / needs_reauth / revoked / error), role, calendar_email, scopes_ok (false = reconnect with connect_calendar), last_error, connected_at. Poll this after connect_calendar; the host is bookable with busy-time awareness once a row is active with scopes_ok=true. |
disconnect_calendar | calendars:write | Write | connection_id, confirm | confirm: true | Revoke a host’s calendar connection (the agent stops reading their busy time and creating events there; Outlook accounts are also removed from the provider). Preview-then-confirm: call with confirm=false first — returns { requires_confirm: true, connection } and changes NOTHING; show the operator whose calendar it is, then call again with confirm=true. Reads the connection back after revoking. |
Availability (5)
Availability (5)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_host_availability | people:read | Read | user_id | No contract flag | Read a team member’s booking availability: timezone (host’s own or seeded from the account), schedule flags, weekly slots grouped by weekday (0=Sunday), blocked dates, and slot_count. A host with slot_count 0 and no Calendly cannot be booked (BOOKING_AVAILABILITY_REQUIRED). |
set_host_schedule | calendars:write | Write | user_id, slots | No contract flag | Replace a team member’s whole weekly schedule in one call (atomic: the previous slots are removed). slots = [{ weekday 0-6, start ‘HH:MM’, end ‘HH:MM’ }] — e.g. Monday-Friday 09:00-17:00 is five entries with weekday 1..5. Overlapping slots on one weekday, end before start, or an unknown IANA timezone are refused with the offending field. timezone defaults to the host’s current one, else the account timezone (seeded). Returns the persisted availability; readiness recomputes and BOOKING_AVAILABILITY_REQUIRED clears once slot_count > 0. |
set_host_timezone | calendars:write | Write | user_id, timezone | No contract flag | Set (or clear with null) a team member’s availability timezone without touching their slots. Unknown IANA zones are refused. Setting a zone also turns respect_own_timezone on unless you pass it explicitly. Returns the persisted availability. |
set_host_blocks | calendars:write | Write | user_id | No contract flag | Add and/or remove a team member’s blocked dates (time off). add = [{ date ‘YYYY-MM-DD’, start?, end?, all_day?, reason? }] (omit times for all-day); remove = block ids from get_host_availability. Applied in order (removes first, then adds); returns the persisted availability with the final block list. |
auto_assign_solo_host | workflows:write | Write | workflow_id | No contract flag | Make a team member the meeting host of an appointment workflow and cover all its meeting types — the one-call setup for a single-host account. Omit user_id when the account has exactly one eligible member (owner/admin/human_agent); with several, the backend answers MULTIPLE_HOSTS with candidates and you pass user_id. Idempotent. Returns the assignment plus that host’s availability; follow with set_host_schedule and connect_calendar if slot_count is 0 or no calendar is active. |
Calendly (9)
Calendly (9)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
connect_calendly | workflows:write | Write | None | No contract flag | Start connecting the client’s Calendly account: returns a hosted Composio authorization URL (connect_url, expires in ~10 min) the operator opens to grant access. IMPORTANT — give connect_url to the user DIRECTLY as a clickable link and tell them to open it; the authorization happens entirely on that page and CANNOT be completed in chat. After they finish, Nexor registers the webhook subscription and starts provisioning automatically. Poll get_calendly_status until state=connected (plan_required means the Calendly plan has no webhook support). Fails with already_connected when an active connection exists — pass reconnect=true to re-authorize. Next: list_calendly_event_types, then set_calendly_binding per host per meeting type. |
get_calendly_status | workflows:read | Read | None | No contract flag | Read the client’s Calendly connection: state (not_connected / connected / plan_required / disconnected), webhook health_status, plan_tier, host_mode, last provisioning report (per-host outcomes: wired / needs_mapping / no_event_type / primary_conflict) and the Composio account status. The poll target after connect_calendly and the place to read provisioning results. |
list_calendly_event_types | workflows:read | Read | None | No contract flag | List the connected Calendly account’s event types: uri (the API uri set_calendly_binding needs), name, duration_minutes, active, scheduling_url, kind and owner. Defaults to the connecting user’s event types; pass owner_uri (a Calendly user uri from the provisioning report hosts[].uri) to list another host’s. Only an ACTIVE event type whose duration equals the meeting type’s duration can be bound. |
set_calendly_binding | workflows:write | Write | workflow_id, meeting_type_id, host_user_id, event_type_uri | No contract flag | Bind a Calendly event type to ONE host for ONE meeting type of a workflow (the per-host model; there is no per-workflow link to manage). The backend validates the event type is active and its duration equals the meeting type’s duration_minutes (400 duration_mismatch otherwise), assigns the host to the workflow/meeting type if needed, writes the binding and reads it back. The first binding on a workflow switches that workflow to Calendly mode: native booking tools are gated and get_available_slots reads Calendly. Pass event_type_uri=null to clear a host’s binding (the host then appears under excluded_hosts). Repeat per host per meeting type; verify with list_calendly_bindings. |
list_calendly_bindings | workflows:read | Read | workflow_id | No contract flag | Read a workflow’s Calendly wiring: workflow_mode (calendly / native), the per-host bindings (host, meeting type, event_type_uri) and excluded_hosts — hosts assigned to a meeting type WITHOUT an event type (reason no_event_type_binding) who are silently missing from availability until set_calendly_binding fixes them. Optionally filter by meeting_type_id. |
set_calendly_host_mode | workflows:write | Write | mode | No contract flag | Set the Calendly host mode. ‘teams’ is the only value: hosts are the Calendly organization members, each mapped to a Nexor user and wired per meeting type by provisioning. Enqueues a provisioning pass; read the outcome with get_calendly_status or run provision_calendly for an immediate report. |
map_calendly_hosts | workflows:write | Write | calendly_user_uri, host_user_id | No contract flag | Map a Calendly host (calendly_user_uri from the provisioning report hosts[] with outcome needs_mapping) to a Nexor user, or pass host_user_id=null to unassign. The uri must come from the last provisioning report — run provision_calendly first. Fails with primary_conflict when the user already books through another calendar (e.g. Google) and cross_client_primary when they are a host in another client. Enqueues a provisioning pass afterwards. |
provision_calendly | workflows:write | Write | None | No contract flag | Run Calendly provisioning now and return the report: discovers the organization’s hosts, maps them to Nexor users (explicit mapping > email match > needs_mapping), resolves each host’s event type and wires workflow_agent_meeting_types for every workflow in Calendly mode. Report hosts[] outcomes: wired / needs_mapping (→ map_calendly_hosts) / no_event_type (→ set_calendly_binding) / primary_conflict / cross_client_primary. Requires host mode set and at least one workflow bound. Pass wait=false to enqueue instead and poll get_calendly_status. |
disconnect_calendly | workflows:write | Write | None | confirm: true | Disconnect Calendly for the client: deletes the webhook subscription, deactivates the integration and revokes the Composio account. Workflows bound to Calendly stop receiving Calendly bookings; per-host bindings are kept for a later reconnect. Preview-then-confirm: call with confirm:false (or omit it) to get a { status:‘needs_confirmation’ } preview that writes NOTHING; only call again with confirm:true after the operator explicitly confirms. |
Booking settings (8)
Booking settings (8)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_workflow_booking_settings | workflows:read | Read | workflow_id | No contract flag | Read a workflow’s booking settings in one call: default_attendees (people invited to every booked meeting), host_assigner (CRM seller-assigner config) and external_booking (client-owned booker config). Use it before set_default_attendees / set_host_assigner / set_external_booking to see the current state. |
set_default_attendees | workflows:write | Write | workflow_id, attendees | No contract flag | Replace the people invited to EVERY meeting booked on this workflow (e.g. a sales manager who sits in on all calls). Full-state: the list sent IS the list afterwards; send [] to clear. Entries are {email, name?}; emails are validated and deduped. They appear on the next booked event’s calendar invite alongside the host and the lead. |
get_meeting_confirmation_email | workflows:read | Read | workflow_id, meeting_type_id | No contract flag | Read a meeting type’s lead confirmation email settings (email_config) and the list of allowed keys. Use before set_meeting_confirmation_email; the write replaces the whole object. |
set_meeting_confirmation_email | workflows:write | Write | workflow_id, meeting_type_id, email_config | No contract flag | Configure the confirmation email the lead receives when a meeting of this type is booked — subject, title, greeting, CTA label, calendar note, extra sections, disclaimer, sender name, or send_to_lead:false to suppress it. Same keys as the dashboard’s email panel; unknown keys are rejected (400) because the renderer never reads them. Replaces the whole email_config (read it first to keep other fields); null clears it. Use preview_meeting_confirmation_email to see the result before/after saving. |
preview_meeting_confirmation_email | workflows:read | Read | workflow_id, meeting_type_id | No contract flag | Render the lead confirmation email (subject + HTML) for a meeting type with sample data, using a draft email_config if given or the persisted one otherwise. Sends nothing. Use it to show the operator what the lead will receive before calling set_meeting_confirmation_email. |
set_host_assigner | workflows:write | Write | workflow_id | No contract flag | Declare the workflow’s CRM seller assigner: an existing ACTIVE workflow tool whose response names the seller, so bookings go to that seller’s calendar and the lead’s owner mirrors the CRM. tool = the workflow_tools name; id_field = the response field carrying the seller id; external_key = the client_users.external_ids key that maps it to a Nexor user (e.g. hubspot_owner_id), or identity:true when the tool already returns a Nexor user_id. Validates keys and that the tool exists and is active (400 otherwise), writes config.host_assigner, then RUNS the test connection (real call to the client’s endpoint) and returns its report — the config is persisted even when the test fails (ok:false with the failing step) so you can iterate; test:false skips it. clear:true removes the assigner. Never write host_assigner through update_workflow_config. |
test_host_assigner | workflows:write | Write | workflow_id | No contract flag | Dry-run the configured host assigner without touching any lead: calls the assigner tool for real, extracts the seller id (id_field), maps it to a Nexor team member and checks their calendar connection. Returns ok plus per-step checks, the fields the response actually carries (to pick id_field), and the resolved user. Pass tool/id_field/external_key/identity to test a variant before saving with set_host_assigner. |
set_external_booking | workflows:write | Write | workflow_id, external_booking | No contract flag | Declare that the client’s OWN booker (their HTTP tools) owns the calendar for this workflow, so Nexor mirrors every booking into a meeting with reminders and confirmation emails. All three verbs are REQUIRED — tool (create), reschedule_tool and cancel_tool — because an omitted verb is silently never mirrored (phantom meetings); reschedule_tool must differ from tool. fields maps dot-paths in the RAW tool response (data.slots.0.start style, never brackets): starts_at is required, ends_at/meeting_url/external_id/title/timezone optional. Every referenced tool (incl. slots_tool/slots_tools) must exist and be active on the workflow (400 TOOL_NOT_FOUND otherwise). provider defaults to external_booker (auto-gates native booking tools); gohighlevel needs no fields. Replaces the whole section and reads it back; null clears. Never write external_booking through update_workflow_config. |
CRM connections (3)
CRM connections (3)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
connect_crm | workflows:write | Write | provider | No contract flag | Start connecting the client’s CRM (hubspot, zoho, kommo or gohighlevel). Returns connect_url + connection_request_id. IMPORTANT — give connect_url to the user DIRECTLY as a clickable link and tell them to open it: HubSpot/Zoho authorize on the vendor’s consent page; Kommo/GoHighLevel paste their token into a Nexor-hosted form. The credentials are entered on that page only — never ask the user to paste a token in chat, and never claim you connected it for them. Then poll get_crm_connection_status with the connection_request_id until status=completed (last_error explains a rejected attempt; the same link stays valid until expires_at). Reconnecting an already-connected CRM issues a fresh link. |
get_crm_connection_status | workflows:read | Read | provider | No contract flag | Connection state of one CRM provider for the client (connected, status, account/org/location identity, last sync + error — never tokens). Pass connection_request_id (from connect_crm) to also see that issuance: pending | completed | expired | failed with last_error. Use after connect_crm and for “is HubSpot connected” questions; get_integration_status gives the cross-provider summary. |
disconnect_crm | workflows:write | Write | provider | confirm: true | Disconnect the client’s CRM (stops its sync immediately; configuration is kept so a reconnect restores it). Destructive for the live sync: ask the user first and call with confirm=true only after they explicitly agree. Without confirm=true nothing changes and the response explains what would be disconnected. |
HubSpot (10)
HubSpot (10)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
get_hubspot_config | workflows:read | Read | None | No contract flag | Read the client’s whole HubSpot post-connect configuration in one call: connection (hub, auth mode, scopes, sync health), default_workflow_id, per-field outbound mappings (field_mappings), the stage → lifecyclestage mapping (lifecycle_mapping), list routing rules (HubSpot list → workflow), deal_sync settings, sync_back, and owner_mappings (team members with the HubSpot owner id stored on them under external_key). Fails with not_connected until connect_crm(provider=hubspot) completes. Start here before any set_hubspot_* call so you never overwrite what the dashboard configured. |
set_hubspot_field_mappings | workflows:write | Write | mappings | No contract flag | Write outbound field mappings (Nexor field → HubSpot contact property), one row per nexor_field; rows not listed are left untouched. hubspot_property=null removes that field’s mapping. Optional per row: nexor_field_type (text/number/date/boolean…), write_once (never overwrite a value already in HubSpot), value_mapping ({ nexor_value: hubspot_option }), is_active, hubspot_property_label, hubspot_property_type. Use get_hubspot_config first; the stage → lifecycle mapping is set_hubspot_lifecycle_mapping, not this. Returns applied rows and the full read-back. |
set_hubspot_lifecycle_mapping | workflows:write | Write | value_mapping | No contract flag | Set how Nexor stages map onto a HubSpot contact property (default lifecyclestage): value_mapping = { nexor_status_key: hubspot_option_internal_value }, e.g. { qualified: ‘salesqualifiedlead’, booked: ‘opportunity’ }. Pass value_mapping=null to clear. Stage keys come from get_workflow; option values are the HubSpot property’s internal option values. Returns the stored row. |
set_hubspot_list_routing | workflows:write | Write | rules | No contract flag | Replace the HubSpot list → workflow routing with the given rules (FULL replace: a list omitted here stops being routed; pass rules=[] to clear). Each rule: list_id (numeric HubSpot list id, or v3:<id>), workflow_id (the agent that receives contacts entering the list), optional name, recurring (keep pulling new members), pause_on_exit (pause the lead when it leaves the list), sync_interval_minutes (supported intervals only). Read the current rules with get_hubspot_config and send them back with your change — this is not a merge. Then run_hubspot_import(list_id) to pull the current members. |
set_hubspot_deal_sync | workflows:write | Write | enabled | No contract flag | Configure deal sync (HubSpot deal stage changes → Nexor conversions/lost). enabled=true requires pipeline_id (a HubSpot DEAL pipeline id); active_stages are the stage ids that count as won (creates a conversion of conversion_type_id when the deal reaches them), lost_stages the ones that mark the lead lost. Optional workflow_id pins which agent’s leads are swept (default: the connection’s default workflow). Ids are validated against HubSpot (404 pipeline_not_found / stage_not_found with the available ones). Runtime keys (cursor, last_swept_at) are preserved. Returns the stored deal_sync. |
map_hubspot_owners_to_hosts | workflows:write | Write | mappings | No contract flag | Map HubSpot owners (sales reps) to Nexor team members so a lead assigned to a rep routes to that host’s calendar and outbound sync writes the right hubspot_owner_id. Each mapping names the owner (owner_email or owner_id, matched against the portal’s owners) and the member (user_id from list_team, or user_email). Writes the owner id on the member (client_users.external_ids[external_key], the same field the dashboard member form edits, so both resolve the same host). Returns applied, unmatched (owner_not_found / user_not_found) and advisory warnings: no_calendar_connected, calendar_email_mismatch (the member’s connected calendar email differs from the owner’s) — warnings never block. Verify with get_hubspot_config.owner_mappings or list_team. |
install_hubspot_agent_tools | workflows:write | Write | workflow_id, tools | No contract flag | Install platform HubSpot tools on an agent with their fixed parameters — the same rows the dashboard HubSpot agent-tools panel creates, so list_workflow_tools / list_hubspot_agent_tools show them. tools: create_contact (no params), create_communication (no params; optional summary_instructions), assign_owner (optional owner = HubSpot owner id for a fixed owner; omitted = the lead’s Nexor assignee), create_deal (REQUIRES pipeline + stage: deal pipeline/stage ids), update_deal (REQUIRES stage; only if the catalog carries it), sync_lead_stage (REQUIRES lead_pipeline + status_map), sync_deal_stage (REQUIRES pipeline + status_map). Ids are validated against HubSpot and status_map keys against the workflow’s stages (400 missing_fixed_parameter lists what is missing per tool; 404 pipeline_not_found / stage_not_found / lead_pipeline_not_found / owner_not_found return the available ids; 409 tool_unavailable = the connection lacks the scope). Idempotent: re-installing updates the fixed params. A booking agent that must log to HubSpot needs at least create_contact + create_communication. |
list_hubspot_agent_tools | workflows:read | Read | workflow_id | No contract flag | List the platform HubSpot tools installed on a workflow with their fixed config read back in install vocabulary (create_deal → pipeline/stage; sync_*_stage → pipeline or lead_pipeline + status_map; assign_owner → owner_source; create_communication → summary_instructions), plus is_active, inherited, auto_run_on_entry, tool_available_for_agent and available_in_statuses. Also returns installable (the tool names) and fixed_parameters (required/optional per tool). Same rows list_workflow_tools shows, filtered to HubSpot. |
run_hubspot_import | workflows:write | Write | None | confirm: true | Import HubSpot contacts into Nexor as leads. Exactly one selector: list_id (a routed or any HubSpot list; queued in the background, returns job_id), contact_ids (specific contact ids; runs inline and returns stats), or all=true (every contact in the portal; queued, 409 import_in_progress if one is running). Optional workflow_id pins the agent the new leads enter (default: the list’s routing rule, else the connection’s default workflow; null = no auto-assignment). This creates leads and may start outreach — it is an operational run: call with confirm:false (or omit it) to get a needs_confirmation preview that does nothing; only pass confirm:true after the operator explicitly approves. Track with get_hubspot_sync_status. |
get_hubspot_sync_status | workflows:read | Read | None | No contract flag | Read HubSpot sync health: connection (sync_status, last_sync_at, last_sync_error, token_expired), inbound_enabled, import_all progress, per-list import stats for routed lists, deal_sync sweep state (last_swept_at, rate_limited_until), last_7_days counts per direction/status and recent_failures (event, error, contact). The poll target after run_hubspot_import and the first read when a client says HubSpot is not syncing. |
Cloud Functions (13)
Cloud Functions (13)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
describe_cloud_functions | workflows:read | Read | None | No contract flag | Explain Cloud Functions: supported triggers, runtime globals, buffered effects, environment variables, deployment, and manual-run safety. Call before authoring code. |
list_cloud_functions | workflows:read | Read | None | No contract flag | List Cloud Functions with trigger, active/deployment health summary, and last-run metadata. |
get_cloud_function | workflows:read | Read | function_id | No contract flag | Get one Cloud Function, including its currently saved source code. |
create_cloud_function | workflows:write | Write | name, trigger_event, code | No contract flag | Create and deploy an event-driven Cloud Function. Read the triggering lead from ctx.lead/lead and use native effects instead of refetching it through the public leads API. Start inactive while validating code, then activate with update_cloud_function. |
update_cloud_function | workflows:write | Write | function_id | No contract flag | Update and, when executable state changes, redeploy a Cloud Function. Source changes replace the entire saved code string. |
delete_cloud_function | workflows:write | Write | function_id | Operator approval in instructions | Permanently delete a Cloud Function, its run history, and its deployed Worker. Destructive: confirm with the operator first. |
run_cloud_function | workflows:write | Write | function_id | No contract flag | Test the saved/deployed Cloud Function with a ctx payload. Always dry-run: proposed effects are recorded but no Nexor lead mutations are applied. |
list_cloud_function_runs | workflows:read | Read | None | No contract flag | List compact Cloud Function run summaries; filter by function, lead, trigger source, or status. |
get_cloud_function_run | workflows:read | Read | run_id | No contract flag | Get full Cloud Function trace detail: ctx, logs, result/error, network requests, proposed/applied effects, and lead snapshots. |
clear_cloud_function_runs | workflows:write | Write | None | Operator approval in instructions | Permanently clear Cloud Function run history, optionally for one function. Destructive: confirm with the operator first. |
list_function_environment_variables | workflows:read | Read | None | No contract flag | List names and timestamps of client-wide, write-only environment variables shared by Cloud and Scheduled Functions. Values are never returned. |
set_function_environment_variable | workflows:write | Write | key, value | No contract flag | Create or replace a shared secret. Key must match ^[A-Z][A-Z0-9_]{0,63}$. The value is encrypted, never echoed, and triggers redeployment of active Cloud/Scheduled Functions. |
delete_function_environment_variable | workflows:write | Write | key | Operator approval in instructions | Delete a shared environment variable and redeploy active functions without it. Destructive and may break code that reads env.KEY; confirm first. |
Scheduled Functions (12)
Scheduled Functions (12)
| Tool | Scope | Mode | Required top-level input | Safeguard | Canonical purpose |
|---|---|---|---|---|---|
describe_scheduled_functions | workflows:read | Read | None | No contract flag | Explain Scheduled Functions, cohort lookup syntax, runtime/effects, scheduling, and the important manual-run mutation behavior. |
list_scheduled_functions | workflows:read | Read | None | No contract flag | List Scheduled Functions with cron, timezone, next/last run, active state, and cohort cap. |
get_scheduled_function | workflows:read | Read | function_id | No contract flag | Get one Scheduled Function including saved source code and lookup configuration. |
create_scheduled_function | workflows:write | Write | name, cron_expression, timezone, code | No contract flag | Create and deploy a cron function. Put lead filtering/order/limit in lookup_config, preview the schedule and cohort, then make code consume ctx.leads; never refetch that cohort through the public leads API. Start inactive for risky effects. |
update_scheduled_function | workflows:write | Write | function_id | No contract flag | Update schedule, cohort, code, metadata, or active state. Code/reactivation deploys; cron/timezone changes recompute next_run_at. |
delete_scheduled_function | workflows:write | Write | function_id | Operator approval in instructions | Permanently delete a Scheduled Function, run history, and Worker. Destructive: confirm first. |
preview_scheduled_function_schedule | workflows:read | Read | cron_expression, timezone | No contract flag | Validate a five-field cron in an IANA timezone and return its next 1-10 UTC occurrences. |
preview_scheduled_function_cohort | workflows:read | Read | lookup_config | No contract flag | Resolve a lookup_config without running code. Returns matching count and bounded sample so you can verify who would enter ctx.leads. |
run_scheduled_function | workflows:write | Write | function_id, confirm | confirm: true | Run a Scheduled Function immediately. WARNING: unlike Cloud Function tests, this applies buffered effects to real tenant data. Only call after the operator explicitly confirms; pass confirm:true. |
list_scheduled_function_runs | workflows:read | Read | None | No contract flag | List Scheduled Function execution summaries by function/status with pagination. |
get_scheduled_function_run | workflows:read | Read | run_id | No contract flag | Get a full Scheduled Function trace: resolved cohort context, logs, network, result/error, and applied/failed effects. |
clear_scheduled_function_runs | workflows:write | Write | None | Operator approval in instructions | Permanently clear Scheduled Function run history, optionally for one function. Destructive: confirm first. |
Pinned source and regeneration
Generated fromgetnexor-ai/nexor-public-mcp at 422ca3501bc7e73d00150211c2ef0e6b84cb4c27, using docs/tool-manifest.json (SHA-256 0bcd54c517788ca7e74fb6e6e193f70d5b0f8b9791b8154bf1f2a114d2801125).
After checking out that exact commit locally:
npm run mcp:snapshot -- /absolute/path/to/nexor-public-mcp
npm run mcp:check -- /absolute/path/to/nexor-public-mcp