Skip to main content
DMLY runs a Model Context Protocol (MCP) server. Point an MCP-capable AI client at it (Claude, Claude Code, Cursor, or your own agent) and the client calls DMLY tools directly: search contacts, read a conversation, find a free slot, raise an invoice. You do not write the HTTP layer, you do not describe the schema to the model, and you do not maintain a glue service. The server tells the client what it can do and the client picks the tool. If you are setting this up for yourself rather than building on it, Connect Claude is the shorter, non-technical version of this section.

The endpoint

The Connect an AI assistant panel in Configurations → Integrations → REST API shows that address ready to copy, once the workspace has a key with MCP access. It shows the platform address above for every workspace, including one whose people reach DMLY on an agency’s own domain. Connecting over OAuth uses the same address and needs nothing minted first, so if that is your route, the panel never appears and you do not need it: the address above is the whole thing you give the client.

What the server can do

Tools are task-shaped, not one-per-endpoint, and responses are trimmed for a model’s context rather than mirroring REST payloads. Every one of the 37 is listed in the tools reference; this is the shape of the surface. Workspace. An orientation snapshot: business name, timezone, which channels are connected and whether each can actually send right now, contact usage, open conversations, today’s bookings. A client is told to call this first. Contacts and CRM. Search people by name, phone, email, handle, tag, saved segment or lifecycle stage; pull one contact’s full picture (tags, custom fields, reachable channels, recent notes, upcoming bookings, unpaid invoices). Writing adds contacts, corrects details, applies and removes tags, writes internal notes, and moves someone between lead, client, inactive and archived. Conversations. Triage the inbox by open/done, unread or platform, and read a thread as a transcript. Writing assigns a conversation to a teammate or marks it handled. Messaging. Send a freeform reply on whichever channel the contact last spoke on, or send one of the workspace’s Meta-approved WhatsApp templates when the 24-hour window has closed. Appointments. Find open slots for a service (the same availability engine the booking page uses, so notice periods, buffers, time off and the connected calendar all apply), list the diary, then book, reschedule or cancel. Finance. List invoices and what is still owed, summarise revenue for a period, create a draft invoice, deliver it to the client on WhatsApp, and record money already received. Broadcasts. Work out exactly who a broadcast would reach and freeze that audience, then queue it for a human to approve. Nothing goes out from here (see below). Automations. List flows and quick automations with their trigger, channel and run counts, inspect individual runs to see why one failed or parked, and switch an automation on or off.

MCP or the REST API?

Both hit the same platform and the same workspace-scoped data, and both accept a dmly_ key; MCP also accepts an OAuth sign-in, which the REST API does not. They are built for different callers. Use the REST API when you know the exact sequence of calls. Use MCP when you want a model to work it out. Nothing stops you doing both from the same workspace with two different keys.

The safety model

This is the part worth reading before you connect an agent to a real workspace. The workspace comes from the credential, never from a tool argument. No tool accepts a workspace id. With an API key, the key is the workspace, and it also remembers the member who generated it: removing that teammate from the workspace stops the key reaching /mcp at all (every request answers 403), though the same key keeps working on the REST API, so revoking the key is the one lever that stops both. With OAuth, the workspace is chosen by a human on the consent screen and recorded against that (user, client) pairing; a token whose pairing has no recorded consent is refused rather than defaulted to the user’s first workspace, and membership is re-checked on every request, so someone removed from a workspace loses access without anyone having to delete the connection. What a connection may do depends on how it authenticated, and either way the limit is enforced by tools not existing. A tool a connection is not entitled to is never registered: it is absent from tools/list, and calling it by name anyway answers not found, because the server resolves a call through the same eligibility check as the listing. The model has no send button to reach for and cannot be talked into finding one.
  • An API key carries mcp:read, mcp:write, or neither. Keys minted before MCP existed carry neither and stay REST-only. mcp:read registers up to 16 tools, mcp:write up to all 37 and implies read. The scope is a ceiling rather than the whole story: a key generated in DMLY records the member who generated it, and on this path it resolves that member’s dashboard permissions too, so it can never reach a tool they could not use themselves. Their role is read live on every request, so narrowing it narrows the key on its next call. Most keys are generated by an owner or an admin, who hold every permission, and for those the scope really is the only limit; a key generated by a custom role is bounded by that role as well.
  • An OAuth connection carries a single scope, mcp:use, and that scope authorises nothing on its own; the team role is the whole gate. The connection acts as the person who approved it and gets exactly the tools their role grants in the dashboard: an owner or admin gets all 37, a plain member gets 33 (everything except the four team-diary appointment tools), a viewer gets 15 reads, a custom role gets what is ticked for it. A connection cannot reach a tool its owner’s role does not grant, and narrowing someone’s role narrows their connection with it. See What an OAuth connection may do.
That extends to customer PII: if the acting member’s role withholds contacts.view_phone or contacts.view_email, phone numbers and email addresses come back masked over MCP too, just as they are in the contact list, the inbox and the mobile app. This applies on both paths: over OAuth the acting member is the person who connected, and on a key it is the member who generated it. Masking covers the contact a write tool echoes back as well, so create_contact and update_contact cannot be used to read a withheld number out. The REST API is the exception: the same key masks nothing there.
Broadcasts always need a person. submit_broadcast does not send. It creates the broadcast marked as requiring approval, returns sent: false, and says so. Somebody with permission has to approve it in the DMLY dashboard before anyone is messaged. That flag is not an input the model can set. The audience is the exact recipient list frozen by preview_broadcast, so it cannot drift between the preview a human read and the send. Outbound messages are budgeted, and metered where they actually leave. Every message an AI-initiated call causes is counted at the send chokepoint, not in the tool, so the confirmation that goes out when the model books an appointment, and the three messages an automation fires off the back of a tag, all count too. There is an hourly send cap and a rolling 24-hour cap on distinct people reached, per workspace. Past either one, sends are refused with a message naming the limit and telling the user to finish from the dashboard. Customer text is fenced. Message bodies, notes, custom-field values and captured flow answers arrive wrapped in an <untrusted_customer_content> tag, and the server’s instructions tell the model to report what is inside it, never to follow it. Writes are audited. Every write is recorded against the workspace’s audit log as mcp.<tool name>. Over OAuth the row is attributed to the person who connected. On a key nobody is signed in, so Who reads System, and nothing shown on the row identifies the key or the member whose permissions it resolved. What names the client is the mcp.session_started row written when it connected, so read that alongside the writes that follow it. See Audit log.
A confirm: true argument on a write tool is a checkpoint, not a proof; the model supplies it. What actually bounds the damage is what the connection was registered with (the key’s scope and the role of the member who generated it, or the connecting person’s role), the outbound budget, and the dashboard approval on broadcasts. Give mcp:write (or an OAuth connection made by someone who can message customers) only to a client you would trust with your inbox.

Plan gating and availability

The MCP server is a plan feature, and a plan that doesn’t set it either way is treated as including it. A workspace whose plan explicitly excludes it gets 403 on every request with Your plan does not include the MCP server. This is not one of the rows in the Change subscription plan panel, so you cannot confirm it there: if you need to know before buying, ask whoever sells you DMLY, or your agency for a sub-account. See What a plan governs. DMLY can also take the MCP server offline fleet-wide during an incident, without revoking anyone’s key. That returns 503 with The MCP server is currently unavailable. It is not something you have done wrong, and it clears on its own.

Integrating MCP clients

Sign in with OAuth or mint a scoped key, then read the failure responses.

MCP tools reference

All 37 tools and 4 resources, with arguments, key scope and team permission.

REST API

The full endpoint surface for your own integrations.

Connect Claude

The same server, explained for the person running the business.