# CommSync MCP — Agent Reference # Fetch this document to understand how to connect to and use CommSync. ## What is CommSync? CommSync is a unified communications platform that merges SMS, email, and phone channels into a single inbox. Contacts ("People") own multiple "Identities" (phone numbers and email addresses) so conversations across channels are unified under one thread per person. Key concepts: - Person: a contact. Owns one or more Identities. - Identity: a single channel handle (PHONE or EMAIL). Belongs to a Person or is an "orphan." - Thread: groups Messages. Can contain both SMS and email from the same Person. - Label: a tag applied to People or Identities for organization. Identity merging is manual only — there is no automatic merge or review queue. Use the merge_identities / split_identity / attach_identity_to_contact / promote_identity_to_contact tools below (see the Contacts section) to reshape a contact graph; nothing merges on its own. ## Connection Endpoint: https://commsync-server-63391657323.us-central1.run.app/api/mcp Transport: HTTP (POST only, stateless) Auth: Bearer token in the Authorization header Example: POST https://commsync-server-63391657323.us-central1.run.app/api/mcp Authorization: Bearer csk_ Content-Type: application/json API keys use the format csk_<48-hex-chars>. Create one in CommSync Settings → API Keys. ## Channel scoping Every request is executed as the key's owner, gated by their org role and channel access. On top of that, a key may be CHANNEL-SCOPED: restricted to specific lines (email accounts / phone numbers) chosen at creation or edit time in Settings → API Keys. If your key is scoped: - Thread/message reads only return conversations on the chosen lines. - send_sms / send_email / compose_* / forward_message can only send FROM the chosen lines (auto-picked defaults also stay in scope). - list_phone_numbers / list_email_accounts only show the chosen lines. - Everything per-user (contacts, labels, AI, webhooks, profile) works unchanged — those surfaces aren't tied to a channel. A scoped key never gains access when its owner loses it; deleted or revoked lines silently drop out of scope. Keys default to ALL channels (full access, including lines connected later) unless explicitly scoped. ## CommSync Agents are not exposed here Agents — the AI teammates that auto-answer inbound texts and email — are configured through the CommSync app (or its REST admin API), not through this MCP tool catalog. There is no tool to create, reconfigure, or approve turns for an Agent. Use send_sms / send_email below if your own integration should reply directly instead. ## Available Tools (75) ### Thread Management (17 tools) Org-scoped reads (filtered by channel access); mutations write only the caller's own per-user view state (ThreadUserState). hard_delete_thread is the only one that touches the shared, canonical record. list_threads List conversation threads, newest first. Supports cursor pagination. Params: cursor? (string), limit? (number, default 50, max 100), view? ("all-channels"|"threads"|"conversations"|"all-conversations"| "archived"|"spam"|"promotions"|"automated"|"deleted"| "inbox"|"contacts"), archived? ("false"|"true"|"all" — legacy, prefer view), labelId?, toPhone? (string[]), toEmail? (string[]), unreadOnly? (boolean), channelId? get_thread_summary Get a single thread summary in the same shape as a list_threads row. Params: threadId (string, required) get_thread_messages Get messages for a thread, ordered oldest first (or newest-after for polling). Params: threadId (required), afterId? (poll — messages after this id), before? (backfill — messages before this id), limit? (1–200, default 50) mark_thread_read Clear the caller's unread count on a thread. Params: threadId (required) mark_thread_unread Force a thread unread for the caller. Params: threadId (required) archive_thread Archive a thread for the caller (per-user). Params: threadId (required) unarchive_thread Unarchive a thread for the caller. Params: threadId (required) mark_thread_spam Move a thread to Spam for the caller (per-user). Params: threadId (required) mark_thread_promotions Move a thread to Promotions for the caller (per-user). Params: threadId (required) mark_thread_automated Move a thread to Automated Messages for the caller — non-human mail: receipts, codes, alerts (per-user). Params: threadId (required) move_thread_to_inbox Clear Archived/Spam/Promotions/Automated flags so the thread shows in Inbox. Params: threadId (required) snooze_thread Snooze a thread for the caller until a future timestamp. Params: threadId (required), until (ISO 8601 timestamp, must be in the future) unsnooze_thread Clear a snooze on a thread for the caller. Params: threadId (required) delete_thread Soft-delete a thread for the caller (moves to Trash). The canonical thread is preserved for other users on shared channels. Params: threadId (required) restore_thread Restore a soft-deleted thread for the caller. Params: threadId (required) delete_message Hide a single message from the caller only. The canonical message persists for other users on shared channels. Params: threadId (required), messageId (required) hard_delete_thread OWNER/ADMIN only. Permanently delete the canonical thread and its messages — affects every user with access. IRREVERSIBLE. Params: threadId (required) ### Outbound messaging (7 tools) Channel access is enforced before any send. Compose/forward also require at least one accessible channel of the right kind. RATE LIMITS ARE ABSORBED HERE. CommSync accepts every authorised send and owns delivery from that point, including carrier / mail-host rate limits. These tools never return 429 and never ask you to retry. Each send returns a receipt: { accepted, messageId, threadId, state, estimatedSendAt, pacing, note } state: dispatching | waiting_on_line | scheduled | sent | failed A "waiting_on_line" message is queued and WILL go out — do not send it again. Call get_send_capacity before a bulk run to pace yourself; exceeding the sustainable rate is safe (messages queue rather than fail) but slower. send_sms Send an SMS reply in an existing thread. Params: threadId (required), identityId (required — must be PHONE kind), smsBody (required), fromPhoneNumberId? (string) send_email Send an email reply in an existing thread. Params: threadId (required), identityId (required — must be EMAIL kind), subject?, bodyHtml?, bodyText?, emailAccountId? compose_sms Start a new SMS conversation. Creates identity and thread if needed. Params: to (required, E.164 format e.g. +15551234567), smsBody (required), displayName?, fromPhoneNumberId? compose_email Start a new email conversation. Creates identity and thread if needed. Params: to (required, email address), subject?, bodyHtml?, bodyText?, displayName?, emailAccountId? forward_message Forward a message to a new recipient. Params: threadId (required), messageId (required), to (required), kind (required: "PHONE"|"EMAIL"), comment? resend_failed_message Re-queue a failed outbound message for retry. Resets the rate-limit budget. Params: threadId (required), messageId (required) get_send_capacity Sustainable send rate for every line you can send from, plus any line currently paused by a provider rate limit and how many messages are waiting. Params: none ### Channel management (7 tools) List tools are scoped to accessible channels. add_* routes through the unified link-channel service (dedup + cross-org uniqueness + tier-cap). Delete tools are OWNER/ADMIN only. list_email_accounts List email accounts the caller can access (credentials never returned). No params. add_email_account Add an IMAP/SMTP email account. Tests connectivity before saving. Params: label (required), emailAddress (required), imapHost (required), imapPort (required), imapSecure (required), smtpHost (required), smtpPort (required), smtpSecure (required), username (required), password (required) delete_email_account OWNER/ADMIN only. Delete an email account. Historical messages preserved. Params: emailAccountId (required) test_email_account Test stored IMAP/SMTP credentials. Params: emailAccountId (required) list_phone_numbers List phone numbers the caller can access. No params. add_phone_number Add a phone number to the caller's org. Params: number (required, E.164), label?, isDefault? (boolean; legacy org flag — sorts the line to the top of a picker only, it does not choose the send line), provider? ("SKYETEL"|"JUSTCALL"|"TWILIO", default SKYETEL), justCallAccountId? (required when provider=JUSTCALL), twilioAccountId? (required when provider=TWILIO; must belong to your workspace) delete_phone_number OWNER/ADMIN only. Delete a phone number. Historical messages preserved. Params: phoneNumberId (required) ### Billing (4 tools) Read state is open to any member. Changing tier or seats, and previewing a seat change, are OWNER only. get_billing_state Tier, status, seats, caps, usage, and whether the caller is the owner. No params. change_tier OWNER only. Swap subscription tier; prorates immediately. Params: tier (required: "STARTER"|"PRO"|"POWER") set_seats OWNER only. Set the paid seat quantity; prorates immediately. Params: seats (required, positive integer) seats_preview OWNER only. Dry-run the prorated charge for a target seat count. Params: seats (required, positive integer) ### Contacts (10 tools) The contact graph (Person + Identity) is private per user — these tools never leak across users in the same org. Merging is always explicit — there is no automatic merge or confidence-scored review queue. list_contacts List all contacts with their identities (max 500). No params. get_contact Get a single contact by ID with identities and labels. Params: personId (required) create_contact Create a new contact. Params: displayName (required, 1–200 chars), notes? (max 5000 chars) update_contact Update a contact. Params: personId (required), displayName? (1–200 chars), notes? (string|null) delete_contact Delete a contact. Identities become orphans. IRREVERSIBLE. Params: personId (required) merge_contacts Merge whole contacts into one survivor. Every channel, label, note, team membership, shared-contact promotion, and conversation moves to the survivor; the emptied source contacts are DELETED. Survivor keeps its name. Manual-only and user-scoped — all contacts must belong to the key owner. Params: survivorPersonId (required), sourcePersonIds (required, 1-10 ids) merge_identities Merge two identities under one contact. Params: identityAId (required), identityBId (required) split_identity Detach an identity from its contact, creating an orphan thread. Params: identityId (required) attach_identity_to_contact Attach an orphan identity to an existing contact. Params: personId (required), identityId (required) promote_identity_to_contact Promote an orphan identity to a new contact. Params: identityId (required), displayName (required, 1–200 chars), notes? ### Identities (4 tools) get_identity Get a single identity with its associated contact. Params: identityId (required) update_identity_notes Update notes on an identity. Params: identityId (required), notes (required, string|null) list_orphaned_identities List identities not attached to any contact (max 500). No params. list_all_identities List all identities with their associated contact (max 500). No params. ### Labels (6 tools) list_labels List all labels with usage counts. No params. create_label Create a new label. Params: name (required, 1–50 chars), color (required, hex e.g. "#F97316") update_label Update a label. Params: labelId (required), name?, color?, aiManaged? (boolean), aiPrompt? (string|null, max 400 chars) delete_label Delete a label. Removes it from all contacts/identities. Params: labelId (required) assign_label Assign a label to a contact OR identity (provide exactly one target). Params: labelId (required), personId? (string), identityId? (string) unassign_label Remove a label from a contact OR identity. Params: labelId (required), personId? (string), identityId? (string) ### Search (2 tools) search Search across contacts, identities, and messages. Params: q (required, 1–200 chars), limit? (1–50) search_threads Full thread-centric search: every conversation matching a query, ranked + paginated, with match snippets. Params: q (required, 2–200 chars), scope? ("everywhere"|"inbox"|"archived"|"automated"|"promotions"|"spam"|"trash"), kinds? (array of "EMAIL"|"SMS"), hasAttachment? (boolean), cursor? (from a prior page), limit? (1–50) ### AI Features (9 tools) get_ai_settings Get AI feature configuration. No params. update_ai_settings Update AI settings (label classifier, daily digest). Params: enabled?, timezone?, reasoningEffort? ("LOW"|"MEDIUM"|"HIGH"), labelClassifyEnabled?, labelClassifyDebounceMs?, labelClassifyReasoning?, dailyDigestEnabled?, dailyDigestCron?, dailyDigestMinPriority?, dailyDigestReasoning?, dailyDigestIncludeSms?, dailyDigestIncludeEmail?, dailyDigestEmailAccountId? get_todays_digest Get the AI daily digest for today or a specific date. Params: date? (YYYY-MM-DD) list_digests List recent AI daily digests. Params: days? (1–90, default 30) dismiss_digest Mark a digest as dismissed. Params: digestId (required) trigger_digest_run Manually trigger digest generation. Params: date? (YYYY-MM-DD) list_ai_runs List recent AI audit entries. Params: limit? (1–200, default 50), feature? (e.g. LABEL_CLASSIFY, INBOX_CLASSIFY, DAILY_DIGEST) test_ai_connectivity Test connectivity to the AI service. No params. trigger_inbox_backfill Enqueue an inbox-classifier backfill across your historical mail. Classifies each not-yet-classified sender (NORMAL/AUTOMATED/PROMOTIONS/SPAM) and auto-files its threads. No params. ### Account (2 tools) get_profile Get current user profile (id, email, name, createdAt). No params. update_profile Update current user name. Params: name? (string|null, 1–100 chars) ### Webhooks (8 tools) Outbound webhook self-provisioning — register your own callback URL so CommSync pushes events to you instead of you polling. See https://commsync-server-63391657323.us-central1.run.app/docs/webhooks.txt for event payload shapes, signing format, and the retry policy. list_webhooks List the caller's webhook endpoints with tier-cap usage. No params. get_webhook Get a single endpoint by id. Params: endpointId (required) register_webhook Create a new endpoint. Returns { endpoint, secret } — secret shown ONCE. Params: url (required, https), events (required, subset of ["message.received","message.sent","message.status_changed", "thread.created","email.opened","attachment.extracted", "contact.merged","email_account.health_changed", "agent_turn.awaiting_approval","agent_turn.sent"] — see /docs/webhooks.txt for each event's shape), mode? ("concise"|"verbose", default concise), messageHistoryCount? (1–100, verbose mode only, default 20), channelScope? ({ allChannels: boolean, channels: [{ channelType: "EMAIL_ACCOUNT"|"PHONE_NUMBER", channelId }] } — omit for all channels; scoped endpoints only receive events on the listed lines) update_webhook Update an existing endpoint. Setting status="active" on a disabled endpoint resets the consecutive-failure counter. Params: endpointId (required), url?, events?, mode?, messageHistoryCount?, status? ("active"|"disabled"), channelScope? (replaces the channel scope wholesale — see register_webhook) rotate_webhook_secret Rotate the signing secret. The previous secret stays valid 24h. Returns { endpoint, secret } — new secret shown ONCE. Params: endpointId (required) delete_webhook Delete an endpoint and its delivery history. IRREVERSIBLE. Params: endpointId (required) list_webhook_deliveries Paginated delivery log for an endpoint (25/page, newest first). Params: endpointId (required), cursor? (from previous page) resend_webhook_delivery Re-enqueue a specific delivery with a fresh evt_* id. Params: deliveryId (required) ## Resources thread-messages — commsync://threads/{threadId}/messages contact-detail — commsync://contacts/{personId} digest — commsync://digests/{localDate} ## Common Workflows 1. Read inbox: list_threads → get_thread_messages(threadId) 2. Reply to SMS: get_thread_messages(threadId) to find the identityId a message came in on → send_sms(threadId, identityId, smsBody) 3. Send new email: compose_email(to, subject, bodyHtml) 4. Find a contact: search(q: "John") → get_contact(personId) 5. Merge duplicate identities: list_orphaned_identities (or search) → merge_identities(identityAId, identityBId) or attach_identity_to_contact(personId, identityId) 5b. Merge duplicate contacts (two whole Person records for the same human): search(q: "Priya") → confirm both with get_contact(personId) → merge_contacts(survivorPersonId, sourcePersonIds) 6. Organize with labels: create_label(name, color) → assign_label(labelId, personId)