Integration recipes
Four patterns cover almost every integration we see. Each one is a handful of calls, and they compose.
Retarget a printed code
The reason most teams reach for the API at all. A code on packaging, a sticker or a poster keeps working while the campaign behind it moves.
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" }'It takes effect immediately on a published code — there is no republish step — and the response tells you whether the code is live or still a draft.
Attach a QR to a CRM record
Create the code with the record's own URL, publish it, then pull the image straight into a file field. The image endpoint returns bytes inline, so most CRMs accept it as an attachment without an intermediate upload.
const headers = { Authorization: `Bearer ${process.env.MOSAQO_KEY}` };
const created = await fetch('https://api.mosaqo.app/v1/public-api/qr', {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({
name: `Deal ${deal.id}`,
mode: 'dynamic',
contentType: 'url',
content: { targetUrl: `https://crm.example.com/deals/${deal.id}` },
}),
}).then((r) => r.json());
const qrId = created.data.id;
await fetch(`https://api.mosaqo.app/v1/public-api/qr/${qrId}/publish`, { method: 'POST', headers });
const png = await fetch(
`https://api.mosaqo.app/v1/public-api/qr/${qrId}/image?format=png&size=1024`,
{ headers },
).then((r) => r.arrayBuffer());Store qrId on your record. It is the handle for everything later — retarget, archive, analytics.
Issue codes in bulk
One code per product, per table, per asset. Send a CSV, poll the job, then map the results back onto your own rows — each row carries the qrId it produced.
curl -X POST https://api.mosaqo.app/v1/public-api/bulk \
-H "Authorization: Bearer $MOSAQO_KEY" \
-H "Content-Type: application/json" \
-d '{
"contentType": "url",
"mode": "dynamic",
"csv": "name,destination\nTable 1,https://example.com/menu?t=1\nTable 2,https://example.com/menu?t=2"
}'
curl "https://api.mosaqo.app/v1/public-api/bulk/$JOB_ID?limit=500" \
-H "Authorization: Bearer $MOSAQO_KEY"Send "dryRun": true first to validate every row without writing anything.
Embed the image by URL
Airtable, Notion and Sheets display a picture from a link they fetch themselves, so they never send your Authorization header and cannot use the endpoint above. Ask for a signed link instead:
curl -X POST https://api.mosaqo.app/v1/public-api/qr/$QR_ID/image-url \
-H "Authorization: Bearer $MOSAQO_KEY" \
-H "Content-Type: application/json" \
-d '{ "format": "png", "size": 1024 }'Only published codes qualify — a published code is already printed and in the world, while a draft may be an unannounced campaign. Put the returned url straight into an attachment or image field. Archiving or deleting the code stops the link resolving at once, which is how you revoke one that has spread further than you meant.
React to scans
Scans are never pushed: there is no per-scan webhook, and we do not send scan-level analytics to third-party endpoints. Subscribe to scan.aggregate_ready if a rollup is enough, or fetch individual scans yourself on a schedule:
curl "https://api.mosaqo.app/v1/public-api/scans?since=$LAST_SEEN&limit=500" \
-H "Authorization: Bearer $MOSAQO_KEY"Keep the returned pagination.nextCursor and pass it back next time. Ordering is stable, so a poller never re-reads or skips a scan. Which fields appear depends on your workspace's analytics privacy settings — the response lists allowedDimensions so you know what to expect.
If polling is genuinely not workable for you and you need scans delivered as they happen, that is available on request rather than by default.
No-code platforms
Make, Zapier, n8n and Pipedream can all talk to Mosaqo today with a generic HTTP module, and the pieces they need are in place: GET /me as a connection test, cursor pagination for iterators, and webhook subscriptions that a trigger can create and remove by itself.
| They ask for | Use |
|---|---|
| Base URL | https://api.mosaqo.app/v1/public-api |
| Auth header | Authorization: Bearer <your key> |
| Connection test | GET /me |
| OpenAPI import | https://api.mosaqo.app/v1/public-api/openapi.json |
| Instant trigger | POST /webhooks on activation, DELETE /webhooks/{id} on deactivation |
| Polling trigger | GET /qr?updatedSince=…&cursor=… |
The same OpenAPI document imports into Postman and Insomnia, and generates a typed client with openapi-typescript or any OpenAPI generator.
Two rules worth following
- Store `qrId`, not the public URL. The URL is stable, but the id is what every later call needs.
- Archive rather than delete. Deleting is permanent and breaks every printed copy; archiving takes a code offline and can be undone.
Full endpoint details are in the API reference.