A FAST,
BORING API.
For non-MCP clients, every operation is exposed through REST and GraphQL. Same auth, same audit trail. Ship the agent of the month against the same surface.
BORING IS A FEATURE.
In a UI-first CRM, the API tends to arrive after the interface. It covers what someone remembered to expose, lags behind new features, and behaves slightly differently from what the screen does. Integrators learn its quirks the hard way.
A headless CRM has no interface for the API to lag behind. In CMP42 the API is the product. The same operations your MCP tools perform are available over REST and GraphQL, against the same models, with the same validation.
We call it boring on purpose. Predictable resources that match your ontology. Errors that tell you what went wrong. No hidden behaviour that only happens when a human clicks. The interesting part should be what you build on top — the Slack bot, the internal app, the agent your team ships next month.
THREE SURFACES. ONE CONTRACT.
Every operation, everywhere
For non-MCP clients, every operation is exposed through both REST and GraphQL. You never have to switch surfaces because one of them is missing a feature.
Same auth model
MCP, REST and GraphQL share one auth model. Permissions that apply on one surface apply on all of them — there is no side door.
Same audit trail
Every write is versioned and records the human or agent that produced it — whether it came from Claude via MCP, a Slack bot via REST or your app via GraphQL.
SAME DATA. YOUR CHOICE OF SHAPE.
The snippets illustrate the same job on both surfaces: find the deals at the renewal stage, then write a note on a company.
With REST, you address resources and use HTTP verbs — easy to call from any language, easy to debug with curl.
With GraphQL, you ask for exactly the fields you need and follow typed edges — the deal’s owner, its company — in a single round trip.
Paths, field names and the header format are illustrative. The concrete API reference ships with the Alpha and is shaped together with the Design Partners.
GET /deals?stage=renewal
Authorization: Bearer <token>
200 OK
{ "data": [
{ "id": "cmp42://deal/…",
"title": "Aspen · Q4 renewal",
"stage": "renewal",
"owner": "cmp42://contact/…" } ] }
POST /companies/<id>/notes
{ "body": "Renewal risk: budget owner changed …" }
201 Created · written_by: app:slack-botquery RenewalDeals {
deals(stage: "renewal") {
id
title
owner { id name }
company { id name }
}
}
mutation {
writeNoteOnCompany(company: "cmp42://company/…",
body: "Renewal risk: …") {
id version writtenBy
}
}MCP, REST OR GRAPHQL? PICK BY CLIENT.
Rule of thumb: if a model decides what to call next, use MCP. If your code decides, use REST or GraphQL.
| Aspect | MCP | REST | GraphQL |
|---|---|---|---|
| Best for | AI agents that choose their own next step | Scripts, bots and backend services with a fixed job | Apps that need a specific slice of the graph |
| Typical client | Claude Desktop, Claude Code, Cursor, Cline, custom MCP SDK agents | Your team’s Slack bot, an internal automation, a data job | The internal app or dashboard your engineers build |
| Shape of a call | A tool call with typed input, e.g. list_deals_at_stage | A resource plus an HTTP verb | One query with nested fields across typed edges |
| Strength | The model discovers tools and their schemas at runtime | Works from any language; simple to test and debug | Fetch exactly what you need, no over-fetching |
| Auth & audit | Same | Same | Same |
ONE AUTH MODEL. ONE AUDIT TRAIL.
Parity is not only about which operations exist; it is about how they are governed. Whatever surface a request comes through, it is authorised by the same model and recorded in the same audit trail. Every object is versioned; every write says which human or agent produced it.
That matters the day you switch agents. The Slack bot you retire and the agent that replaces it leave the same kind of record, so your history stays readable across tools and years.
Authentication uses workspace-scoped Bearer tokens: Personal Access Tokens for humans and Service Tokens for agents and bots, each carrying Model-level scopes so a token can be trusted with exactly the operations it needs. The default rate limit is 1000 requests per minute per token — hitting it returns a 429 with Retry-After. The API is versioned as a URL prefix (/api/v1/); breaking changes require a new major version, with at least twelve months of overlap where the previous version keeps working before it is retired.