qrblox-mcp is the JSON-RPC MCP server that gives AI agents safe, merchant-scoped tools.
Sign in at app.qrblox.com/settings, mint a sk-qrblox- key, paste it into Claude Desktop or
Cursor, and the agent can draft QR codes, audio codes, menus, and scan challenges — and read
scan analytics — for one merchant workspace, nothing more.
The Qrblox MCP server (qrblox-mcp) connects AI agents to merchant-owned Qrblox data
over the Model Context Protocol.
A merchant mints an API key at app.qrblox.com/settings,
pastes it into Claude Desktop, Cursor, or a custom agent, and the assistant can draft QR codes,
audio codes, menus, and loyalty challenges — and summarize scan activity — all scoped to that one
merchant workspace.
The fastest path: agents can generate a real, scannable QR code with no key and no OAuth. Sandbox codes work immediately, are IP rate-limited, and expire in 7 days unless claimed into a real account.
curl -X POST https://app.qrblox.com/api/generate-qr-code \ -H "content-type: application/json" \ -d '{"url":"https://example.com"}'
{
"ok": true,
"code": {
"id": "…",
"destinationUrl": "https://example.com",
"qrImage": "data:image/png;base64,…",
"claimToken": "…",
"expiresAt": "2026-06-02T…Z"
},
"upgrade": { "signup": "https://app.qrblox.com", /* … */ }
}
Agents can discover the endpoint without writing code first — GET https://app.qrblox.com/api/generate-qr-code
returns a JSON manifest describing every field, the bulk endpoint, the limits, and the path
to a real key. The MCP itself is reachable at the same host: GET https://app.qrblox.com/api/mcp
returns the full tool list, and the JSON-RPC tools/list method works with no auth.
POST /api/generate-qr-code. Above that — or for bulk and any production use — mint a free API key.sandboxCodes collection and auto-expire after 7 days.claimToken and links to app.qrblox.com to claim and keep them.MCP keys are merchant-scoped and created in the Qrblox portal:
sk-qrblox-<64 hex chars> for your merchant workspace.Keys carry merchant-scoped permissions:
The canonical MCP URL https://api.qrblox.com/mcp/v1 advertises itself as an OAuth 2.1
protected resource (RFC 9728). ChatGPT and Claude custom connectors discover the authorization
server, register dynamically (RFC 7591), and complete PKCE S256. Consent uses the existing
Qrblox portal login (Google / Apple) at https://app.qrblox.com/oauth/consent.
sk-qrblox- API keys keep working unchanged.
GET https://api.qrblox.com/.well-known/oauth-protected-resourceGET https://api.qrblox.com/.well-known/oauth-authorization-server/oauth/register, /oauth/authorize, /oauth/token, /oauth/revoke on api.qrblox.comqrblox_at_, 1 hour). Refresh tokens rotate (qrblox_rt_, 30 days). Authorization codes last 10 minutes. Firestore stores only SHA-256 hashes.
Send the key as a Bearer token. Keys begin with sk-qrblox- and are validated against
Firestore on every request; the matching record carries the merchant id and scopes that the tool
layer enforces. OAuth access tokens begin with qrblox_at_ and map to the same merchant
workspace as the signed-in Qrblox user who clicked Allow.
Authorization: Bearer sk-qrblox-4c2fa8e1b7d9…
The server also accepts a Firebase ID token or a session cookie for in-app callers, and an
OAuth 2.1 access token from the ChatGPT/Claude connector flow. External agents that already
hold a key keep using sk-qrblox-.
Canonical: POST https://api.qrblox.com/mcp/v1. Alias on the portal:
POST https://app.qrblox.com/api/mcp. Standard MCP JSON-RPC 2.0
envelopes in the body, a single JSON-RPC response in the reply. A GET on the same URL
returns server metadata (name, endpoint, docs link). Each POST is stateless — no session handshake
between requests.
POST /api/mcp HTTP/1.1 Host: app.qrblox.com Authorization: Bearer sk-qrblox-… Content-Type: application/json {"jsonrpc":"2.0","id":1,"method":"tools/call", "params":{"name":"qrblox.qr.list", "arguments":{"limit":10}}}
Supported JSON-RPC methods:
initialize — handshake. Returns protocolVersion 2025-03-26 and server info (qrblox-mcp). No auth required.notifications/initialized — accepted as a notification; replies 204 No Content.ping — protocol-level liveness probe; returns an empty result. No auth (distinct from the qrblox.ping tool).tools/list — discover the tool registry. Requires auth.tools/call — invoke a tool by name with { name, arguments }. Requires auth.ok, merchantId, and authMethod.limit (1–100).title, destinationUrl. Optional: campaign, tags, dynamic, template (hexSeal, soundSeal, or round — circular artwork matching qrblox.com/round-qr-code-generator), foregroundColor / backgroundColor / logoUrl (round only), scanBeepSound, boostFrame.title, payloadText. Optional: campaign.name. Optional: description, locationId.limit (1–100).menuId, sectionName, name. Optional: description, price (number).name, challengeType (streak, scan_count, visit_window, or game). Optional: description, rewardText, startsAt, endsAt (ISO dates).days (1–365).Every tool re-derives the merchant from the verified caller, so an agent can only ever read and write within its own merchant workspace.
{
"jsonrpc": "2.0",
"id": "create-menu-qr",
"method": "tools/call",
"params": {
"name": "qrblox.qr.create",
"arguments": {
"title": "Table 12 lunch menu",
"destinationUrl": "https://qrblox.com/m/table-12",
"campaign": "Lunch menu rollout",
"tags": ["menu", "table-12"]
}
}
}
Bulk generation is for real marketing — it writes permanent codes into your
merchant workspace with evergreen Firebase Storage PNG URLs, no sandbox, no expiry. So it
requires a sk-qrblox- key. Mint one for free at
app.qrblox.com/settings.
curl -X POST https://app.qrblox.com/api/bulk-qr-codes \ -H "authorization: Bearer sk-qrblox-…" \ -H "content-type: application/json" \ -d '{"urls":[ "https://qrblox.com/menu/spring", "https://qrblox.com/menu/summer", "https://qrblox.com/menu/fall" ]}'
Up to 50 URLs per call. Response shape: { ok, count, codes: [{ codeId, title, destinationUrl, qrImageUrl }] }.
Each qrImageUrl is a Firebase Storage download URL — stable, sharable, no expiry.
Same shape as bulk, item by item — useful when you want per-row error handling, per-row delay, or richer per-row arguments (campaign labels, tags).
const rows = [ { "title": "Email — June", "destinationUrl": "https://qrblox.com/june", "campaign": "june" }, { "title": "Print — June", "destinationUrl": "https://qrblox.com/june", "campaign": "june" }, ]; for (const args of rows) { await fetch("https://app.qrblox.com/api/mcp", { method: "POST", headers: { "authorization": "Bearer sk-qrblox-…", "content-type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: args.title, method: "tools/call", params: { name: "qrblox.qr.create", arguments: args }, }), }); }
Want all codes labelled by campaign for reporting? Pass a consistent campaign
argument — the scan summary rolls up by campaign.
Qrblox supports two flavors of code, and your tracking story depends on which one you create:
qrb.dev/qr/<uniqueQRCodeID>. Every scanner sends users through Qrblox before forwarding to your URL, so every scan is counted and the destination is re-pointable without reprinting.For account-owned codes, agents can read scan counts via:
{ "jsonrpc":"2.0", "id":1, "method":"tools/call",
"params":{ "name":"qrblox.analytics.scan_summary",
"arguments":{ "days": 30 } } }
Returns totalScans and a byDay series. The deeper raw export is the
daily DataPro report (see Reports on the docs home) —
per-code, with city, lat/lng, and day-of-week.
Concrete patterns agents can run on a merchant's behalf, end-to-end through the MCP:
campaign so you can compare which channels actually drive scans.qrblox.analytics.scan_summary on each to see which converts.bulk-qr-codes (or scripted qrblox.qr.create) call gives you N store-specific codes in seconds — each labelled by location and rolling up in the daily city-rank report.tools/call with qrblox.qr.create — the calls and the code
shape are the same. Sandbox codes can be claimed into the account using the claimToken.
Tool results come back as JSON-RPC result objects; failures come back as JSON-RPC
error objects. Auth failures carry a data.code of NO_AUTH,
INVALID_API_KEY, INVALID_TOKEN, or REVOKED.
method).error.message.{
"mcpServers": {
"qrblox": {
"url": "https://app.qrblox.com/api/mcp",
"headers": { "Authorization": "Bearer sk-qrblox-…" }
}
}
}
In Cursor settings, add a new MCP server with the same URL and Authorization header.
Point the connector at https://api.qrblox.com/mcp/v1. The host returns
WWW-Authenticate with protected-resource metadata; the client registers, the
merchant signs in at app.qrblox.com, and the connector receives a scoped access token. No
sk-qrblox- key is required for that path.
# Expect: { ok: true, merchantId: "…", authMethod: "api-key" } curl -s https://app.qrblox.com/api/mcp \ -H "Authorization: Bearer sk-qrblox-…" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"qrblox.ping","arguments":{}}}'
Looking for the rest of the stack? Web Portal · Canva app · All docs