Skip to main content
These rules apply to every one of the 70 tools, verified against the served JSON schemas and live validation probes. An agent that follows them will rarely hit an avoidable error.

Contract rules the schemas enforce

  • Unknown arguments are invalid. Every inputSchema sets additionalProperties: false. Don’t pass extra keys “just in case” — the call fails validation.
  • Required means non-empty. Required string and array fields reject null, "", and [] alike. In the server’s own words: “null, empty strings, and empty arrays are not accepted for required fields.”
  • Every tool is annotated. readOnlyHint: true (31 tools) or destructiveHint: true (39 tools) arrives in tools/list. Gate on these before adding your own confirmation UX.
  • Pagination is uniform. List/search tools take top (default 20, max 100) and skip; responses return Data.Items plus HasMore. Iterate with skip += top until HasMore is false.
  • Patch semantics on updates. Every update_* tool changes only the fields you pass; omitted/null fields keep their current value. A passed list field replaces the whole list.
  • IDs come from resolvers. Mutating tools require exact numeric IDs from prior search_* / list_* / filter_* output. The descriptions repeat one rule verbatim: “Never invent, estimate, default, or ask the user.”
  • Approvals are addressed by pair. Approve/reject/regenerate identify a Jason draft by sequenceId + contactId — there is no separate draft/message ID.
  • Batches are bounded and explicit. Bulk tools cap at 100 items. reply_bulk_approve_messages is atomic (any stale reference rejects the whole batch; nothing is sent). Contact batches return per-item results (Affected / AffectedContactIds / NotProcessed) — the authoritative record of what actually happened; report exact counts (“reassigned 48 of 50”).

Enum quick reference

Every closed value set the server declares, in one place. All are case-insensitive unless noted.

Response envelopes

Transport. Responses are SSE frames (text/event-stream) whose data: lines carry ordinary JSON-RPC payloads. Read the last data: frame for the result. Success. A successful tools/call returns result.isError = false, and the actual payload is a JSON string inside result.content[0].text:
Error. Tool failures are not HTTP errors — they come back as HTTP 200, JSON-RPC success, with result.isError = true and the error as a JSON string in result.content[0].text:
Parse the inner JSON; branch on Success, then on ErrorCode. Real captured examples:

Error model

Handle failures at three layers:
  1. Transport / auth. 401 (missing/invalid key), 403 (valid key, missing scope or plan feature), 429 (rate limit — back off using X-Rate-Limit-Reset), 5xx (transient).
  2. Tool-level ErrorCodes (inside the envelope): InvalidArguments, NotFound, Forbidden, Conflict, InvalidInput, InvalidParameter, UpstreamFailure, ServiceUnavailable, plus domain-specific codes named per tool (NoEmailAccounts, NoContacts, ContactLimitExceeded, ChannelMismatch, ContactOptedOut, ThreadSendFailed, InvalidStatusTransition, ArticleNotFound, PERMISSION_DENIED, …). Each tool’s description states which codes it can return and what to do about each.
  3. Per-item partial results. Batch tools report per-contact skips in NotProcessed (e.g. ContactAlreadyInSequence, ContactInBlackList, NotFound) while the call as a whole succeeds. Read them; never claim an item succeeded unless it appears in the affected list.

Safe-retry matrix

A timed-out send may already have succeeded server-side. Verify with a read before retrying anything that reaches a prospect.
Next: Building agents for how to sequence these calls, and the tool reference for the full catalog.