MCP
The same API, addressed to an assistant rather than to a scenario. An MCP client chooses what to call while it is running, from the tool descriptions — so what it reaches is exactly what the credential behind it reaches, and every tool is a public API call underneath.
One endpoint
Everything goes to POST /v1/mcp. There is no session to open and no stream to hold: GET and DELETE answer 405, and each message is its own request.
POST https://api.mosaqo.app/v1/mcp
Authorization: Bearer $MOSAQO_KEY
MCP-Protocol-Version: 2026-07-28Protocol revisions 2025-03-26 through 2026-07-28 are all answered. A client that opens with initialize gets the handshake it expects; one that states its version on every request is served that way instead.
Connecting with an API key
The quickest route, and the right one for a tool you run yourself. Any key works as a bearer token, and the tools the assistant is offered are cut to that key's scopes.
Most clients take a block like this one:
{
"mcpServers": {
"mosaqo": {
"type": "http",
"url": "https://api.mosaqo.app/v1/mcp",
"headers": { "Authorization": "Bearer $MOSAQO_KEY" }
}
}
}curl -X POST https://api.mosaqo.app/v1/mcp \
-H "Authorization: Bearer $MOSAQO_KEY" \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/list" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }'Connecting through OAuth
For a connector in somebody else's directory, where the person never handles a key. Discovery follows RFC 9728 and RFC 8414, clients register themselves with RFC 7591, and every authorization uses PKCE with S256.
curl https://api.mosaqo.app/.well-known/oauth-protected-resource
curl https://api.mosaqo.app/.well-known/oauth-authorization-serverAn unauthorized call says where to go and what to ask for:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="Mosaqo MCP",
resource_metadata="https://api.mosaqo.app/.well-known/oauth-protected-resource",
scope="qr:read analytics:read reviews:read exports:read"Whoever approves is signed in to Mosaqo, picks the workspace it applies to, and can only do so where they are an owner or an admin — the same line that decides who may issue an API key. They can disconnect it afterwards under Bulk & API.
What an assistant can do
The scopes you already know, unchanged. A tool the credential does not allow is not listed in tools/list and cannot be called by naming it anyway, so the live answer for any connection is tools/list itself.
| Scope | Grants |
|---|---|
qr:read | List and read QR codes, folders and templates |
qr:write | Create, update, retarget, publish, archive and delete |
exports:read | Download rendered cards |
analytics:read | Aggregate analytics and individual scans |
bulk:write | Create and read bulk jobs |
webhooks:write | Manage webhook subscriptions |
reviews:read | Places, surveys and how they are doing — never the answers themselves |
What it will never reach
What people wrote in a survey. reviews:read opens your places, your surveys and how they are doing — the counts, the scores, the spread of answers per question. The sentences a visitor typed are returned here no more than anywhere else a credential can reach, for the same reason the feedback.* events carry a score and never the words.
Deleting a code. The REST route asks for a typed confirmation because destroying a printed code's redirect cannot be undone, and that is not a confirmation an assistant gives on somebody's behalf. Archiving is offered instead, and it reverses.
Writing is marked as writing. A tool that changes something carries readOnlyHint: false, and archiving carries destructiveHint: true — which is what makes a client stop and ask a person before it runs.