Skip to content

Mosaqo API for developers

A REST API over the same workspace you use in the app. Everything below works with a single API key and any HTTP client.

What you can do

  • Create QR codes from your own records — one at a time, or ten thousand from a CSV.
  • Change where a printed code points, in one call, without reprinting anything.
  • Download the finished card as PNG, SVG or PDF and attach it to a CRM record.
  • Read scans and aggregate analytics, filtered by your workspace's privacy rules.
  • Receive signed webhooks when codes are created, published, changed or archived.

Quickstart

Create a key in Bulk & API inside your workspace, tick the qr:write and qr:read scopes, and copy the secret — it is shown once.

Check the key and learn which workspace it opens:

curl https://api.mosaqo.app/v1/public-api/me \
  -H "Authorization: Bearer $MOSAQO_KEY"

Create a dynamic QR code:

curl -X POST https://api.mosaqo.app/v1/public-api/qr \
  -H "Authorization: Bearer $MOSAQO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Autumn campaign",
    "mode": "dynamic",
    "contentType": "url",
    "content": { "targetUrl": "https://example.com/autumn" }
  }'

Publish it, which is what makes the redirect resolve, then fetch the image you can print:

curl -X POST https://api.mosaqo.app/v1/public-api/qr/$QR_ID/publish \
  -H "Authorization: Bearer $MOSAQO_KEY"

curl https://api.mosaqo.app/v1/public-api/qr/$QR_ID/image?format=png&size=2048 \
  -H "Authorization: Bearer $MOSAQO_KEY" -o campaign.png

Months later, when the campaign moves, retarget the same printed code without reprinting it:

curl -X POST https://api.mosaqo.app/v1/public-api/qr/$QR_ID/destination \
  -H "Authorization: Bearer $MOSAQO_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/winter" }'

Authentication

Send your secret either way. Both are supported for the whole of v1.

Authorization: Bearer mosaqo_xxx
X-Mosaqo-API-Key: mosaqo_xxx

A key belongs to exactly one workspace, so paths do not need a workspace id: /v1/public-api/qr is enough. The older /v1/public-api/workspaces/{workspaceId}/qr form still works.

ScopeGrants
qr:readList and read QR codes, folders and templates
qr:writeCreate, update, retarget, publish, archive and delete
exports:readDownload rendered cards
analytics:readAggregate analytics and individual scans
bulk:writeCreate and read bulk jobs
webhooks:writeManage webhook subscriptions

Keys can be revoked, given an expiry date, and restricted to an IP allow-list. A key rejected by the allow-list is told so explicitly, rather than looking invalid.

Pagination

Lists use keyset pagination. Pass pagination.nextCursor back unchanged; rows never repeat or vanish because something was edited mid-sync, which matters for an overnight sync.

curl "https://api.mosaqo.app/v1/public-api/qr?limit=50&cursor=$CURSOR" \
  -H "Authorization: Bearer $MOSAQO_KEY"

To poll for changes rather than walk everything, add updatedSince=2026-08-05T00:00:00Z.

Errors

Every failure carries a stable code to branch on, a human-readable message, and a requestId worth quoting to support.

{
  "error": "This API key does not have the qr:write scope.",
  "message": "This API key does not have the qr:write scope.",
  "code": "insufficient_scope",
  "details": { "required": "qr:write", "granted": ["qr:read"] },
  "requestId": "req-42"
}
StatusCodesWhat to do
401invalid_api_keyThe key is missing, revoked or expired. Ask the user to reconnect.
403insufficient_scope, ip_not_allowed, workspace_mismatch, quota_exceededThe key is valid but not allowed to do this. Reconnecting will not help.
404not_foundNo such record in this workspace.
409idempotency_conflict, idempotency_in_progressReuse of a key with different input, or a first attempt still running.
422validation_failed, invalid_cursor, approval_required, contrast_check_failedFix the input. Never retry unchanged.
429rate_limitedWait for Retry-After seconds.

Rate limits

Every response carries your current budget, not just the ones that are rejected:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 574
X-RateLimit-Reset: 1785942718

The limit is an anti-abuse guard, not a plan: it implies no quota, billing or upgrade path.

Idempotency

Idempotency-Key is optional. Send one — any unique string — and retrying the identical request within 24 hours replays the original response instead of acting twice. Reusing the same key with different input returns 409. Without a key the request simply runs, with no replay protection.

Next

  • Webhooks — the event catalogue and how to verify a delivery.
  • Recipes — Make, Zapier, n8n and CRM patterns.
  • API reference — every endpoint, with a live console.