Developers
Generating and re-pointing QR codes in Make.com
By Sam Moreton · updated 28 June 2026
10 min read · Updated 28 June 2026
This is the Make path. If you use a different tool, OpenQR ships a dedicated node for n8n and integrates with Zapier; to call the API from your own backend instead, see getting started with the OpenQR API. Make just wraps those same endpoints in visual modules.
Why HTTP modules, not a dedicated app
Make's catalogue doesn't include an OpenQR app, but it doesn't need one: every OpenQR operation is a single HTTP request with an Authorization header and a JSON body. Make's "HTTP → Make a request" module handles JSON endpoints, and "HTTP → Get a file" handles the binary QR image. That covers the entire API.
Get an API key
Everything authenticates with one key. Create a free one at openqr.uk/api — sign in with a magic link, then mint a key (it starts with oqr_). It's shown once and stored only as a SHA-256 hash, so copy it when you see it.
Store the key in a Make connection or data store
An OpenQR key can create, edit and delete every dynamic code on your account. In Make, keep it in a reusable connection/keychain or a Data store — never paste it raw into each module where it ends up in scenario history. To rotate, mint a new key and update the one place it lives.
The building block: an authenticated HTTP request
Every JSON call in this scenario uses HTTP → Make a request configured the same way. Set it once and you can reproduce any endpoint:
| Field | Value |
|---|---|
| Method | POST / GET / PATCH / DELETE (per endpoint below) |
| Headers | Authorization: Bearer oqr_yourkey · Content-Type: application/json |
| Body type | Raw |
| Content type | JSON (application/json) |
| Parse response | Yes — so Make maps the JSON fields for downstream modules |
Step 1: create a dynamic code
An HTTP module pointed at POST https://openqr.uk/v1/dynamic. The body is JSON with a public http(s) destination (private/internal hosts and oqr.to self-loops are rejected). With Parse response on, Make exposes id, slug, short_url and destination to later modules.
// POST https://openqr.uk/v1/dynamic — request body
{
"destination": "https://example.com/launch-soon",
"label": "Summer poster"
}
// Parsed response Make hands you:
{
"id": "b1c2…",
"slug": "k7Pm2qR",
"short_url": "https://oqr.to/k7Pm2qR",
"destination": "https://example.com/launch-soon"
}Persist id and short_url
Pipe the parsed output into a Data store, Google Sheets row or Airtable record. Store id (your handle for re-pointing and analytics) and short_url (the link you print). The printed QR encodes the short_url, so it never changes — id is what every later step needs.
Step 2: render the QR image
The image endpoint returns binary, not JSON, so use HTTP → Get a file against the GET /v1/qr endpoint with the short_url as the data query parameter. Add the same Authorization header. The module outputs a file you can attach to an email, upload to Drive, or send to a label printer.
# HTTP → Get a file
# URL (data = the short_url from step 1, size to taste):
https://openqr.uk/v1/qr?data=https://oqr.to/k7Pm2qR&format=png&size=1024
# Header: Authorization: Bearer oqr_yourkeyURL-encode the data value
Because data is itself a URL, wrap it with Make's encodeURL() function in the mapping so query characters don't break the request — encodeURL(short_url). For a static one-off code, put any text or URL in data and skip step 1 entirely.
Step 3: re-point the code later
The payoff of a dynamic code: a second scenario swaps the destination when the real page is live — no reprint. Use an HTTP module with PATCH https://openqr.uk/v1/dynamic/{id}, substituting the stored id, and a JSON body containing only what changes.
// PATCH https://openqr.uk/v1/dynamic/b1c2…
{ "destination": "https://example.com/launch-live" }
// 200 OK — every future scan now lands on the new URL; the printed code is untouchedRe-point on a schedule
Trigger the PATCH scenario from a scheduled run to flip destinations automatically — point a venue code at today's agenda each morning, or move a packing-slip code from order-tracking to a review request a few days after dispatch. Same physical code, different link over time.
Step 4: read scan analytics
An HTTP module on GET https://openqr.uk/v1/dynamic/{id}/scans?days=30 returns a scans summary plus a fuller analytics object with a zero-filled daily series and top country/device/referrer entries. A common pattern: a weekly scheduled scenario → Get Scans → format → post a digest to Slack or email.
{
"scans": { "total": 1840, "last7": 263, "topCountry": "GB", "topDevice": "mobile" },
"analytics": {
"days_window": 30,
"window_total": 612,
"daily": [ { "day": "2026-06-22", "n": 31 }, … ],
"by_country": [ { "value": "GB", "n": 421 }, … ],
"by_referrer": [ { "value": "Direct", "n": 580 }, … ]
}
}No PII in the numbers
Each scan records a timestamp plus coarse country, device class and referrer host, derived from Cloudflare request headers at redirect time. No IPs, no fingerprints, no per-person identifiers. It tells you where and on what, not who.
Batch-creating codes from a list
Single-create is capped at 20 codes per hour per account (the API returns 429 over that). When a scenario needs many at once — one code per product, table or ticket — call POST https://openqr.uk/v1/dynamic/bulk with a body of { "codes": [ { destination, label }, … ] }, up to 200 per request. Build the array with an Iterator/Array aggregator from a Sheets or Data store source. For the deeper batching patterns see bulk QR code generation.
// POST https://openqr.uk/v1/dynamic/bulk
{
"codes": [
{ "destination": "https://shop.example.com/p/1001", "label": "Mug" },
{ "destination": "https://shop.example.com/p/1002", "label": "T-shirt" }
]
}Handling errors in a scenario
OpenQR returns standard HTTP status codes, which Make surfaces on the HTTP module so you can attach error handlers and routers:
- 400 — bad body or a disallowed destination (private/internal host or an oqr.to self-loop). Fix the input.
- 401 — missing/invalid key. Check the Authorization header.
- 404 — code not found or not owned by your key (wrong
id). - 409 — a custom
slugyou requested is already taken; try another. - 429 — creation rate limit hit; a
Retry-Afterheader tells you when to retry. Add a delay or break the work into smaller runs.
For the endpoint reference your HTTP modules target, the OpenAPI 3.1 spec is at https://openqr.uk/openapi.json — you can import it into tools that accept a spec. To drive the same operations from an AI agent instead of a scenario, see the OpenQR MCP server.