Contract rules the schemas enforce
- Unknown arguments are invalid. Every
inputSchemasetsadditionalProperties: 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) ordestructiveHint: true(39 tools) arrives intools/list. Gate on these before adding your own confirmation UX. - Pagination is uniform. List/search tools take
top(default 20, max 100) andskip; responses returnData.ItemsplusHasMore. Iterate withskip += topuntilHasMoreisfalse. - 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_messagesis 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:
result.isError = true and the error as a JSON string in result.content[0].text:
Success, then on ErrorCode. Real captured examples:
Error model
Handle failures at three layers:- Transport / auth.
401(missing/invalid key),403(valid key, missing scope or plan feature),429(rate limit — back off usingX-Rate-Limit-Reset),5xx(transient). - 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. - 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
Next: Building agents for how to sequence these calls, and the
tool reference for the full catalog.