Create QR codes with the API, MCP, and automations
Create, update, list, and delete QR codes with the CodeQR API, one by one or in bulk, from an AI client through the MCP server, or from Make and webhooks.

At the end of this guide you can create QR codes from your own system — one per order, table, product, or spreadsheet row — change where they point, list and delete them, and get notified of every scan, using the REST API, the MCP server, or Make.
Availability
- Plan: the API is on all plans; the monthly QR code allowance is the same as in the app (Free 5, Starter 150, Pro 1,000 dynamic + unlimited static, Business 5,000). Requests per minute: Free 60, Starter 100, Pro 500, Business 1,000. Per-code scan webhooks need Business.
- Where:
https://api.codeqr.io/qrcodeswith a key from Settings → API Keys; the MCP server athttps://mcp.codeqr.io/mcp; the CodeQR app in Make. Full field reference: docs.codeqr.io.
Before you start
- Create an API key in Settings → API Keys → Create, name it, choose the permissions, and copy the value once. Send it as
Authorization: Bearer codeqr_…. typeis the only required field.staticdefaults tofalse(dynamic).domainandkeyare optional; without them CodeQR uses the workspace domain and a random key ending in-qr.keycannot be reused within the workspace.- Design defaults through the API:
size1024,levelM, black on white,showLogotrue (workspace logo). Send"level": "H"when the code carries a logo — the editor does this for you, the API does not.
Endpoints
POST /qrcodes— What it does: Create one codePOST /qrcodes/bulk— What it does: Create 1 to 100 codes in one call (array body)GET /qrcodes— What it does: List and filter (type,format=static · dynamic,search,tagIds,folderId,sort=createdAt · scans · lastClicked,page,pageSizeup to 100, and more)GET /qrcodes/count— What it does: Count with the same filters, optionalgroupByGET /qrcodes/info?domain=…&key=…— What it does: Retrieve one code by domain and key (there is noGET /qrcodes/{id})PUT /qrcodes/{qrcodeId}— What it does: Update any field, includingurl, design,expiresAt,password,archived, andstatic: falseto convertDELETE /qrcodes/{qrcodeId}— What it does: Delete one codeDELETE /qrcodes/bulk?qrcodeIds=id1,id2— What it does: Delete several (comma-separated ids in one parameter)GET https://api.codeqr.io/qr?url=<shortLink>— What it does: PNG of an existing code (no key needed)
Steps: create codes from your data
- Create one code to check the fields you need:
curl -X POST https://api.codeqr.io/qrcodes \
-H "Authorization: Bearer codeqr_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "type": "url", "url": "https://example.com/menu", "title": "Cafe menu", "static": false }'Response (200 OK, abbreviated): "id": "cmsx875y30001je433cy0ltlh", "key": "iVx0OCFyDn79-qr", "shortLink": "https://qrup.link/iVx0OCFyDn79-qr", "image": "https://res.cloudinary.com/dhnaggn4g/image/upload/v1786970667/qrup.link/iVx0OCFyDn79-qr.png", "scans": 0.
- Create many at once — one object per row, up to 100 per call. Add
externalIdwith your own record id so you can find the code again:
curl -X POST https://api.codeqr.io/qrcodes/bulk \
-H "Authorization: Bearer codeqr_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '[
{ "type": "url", "url": "https://example.com/table/1", "title": "Table 1", "static": false },
{ "type": "url", "url": "https://example.com/table/2", "title": "Table 2", "static": false }
]'Response (200 OK, abbreviated): an array in the same order — "key": "XD2lytoBgPOe-qr" and "key": "IS87MRzr7Gos-qr", each with its shortLink. Bulk-created codes come back with "image": null; render them with https://api.codeqr.io/qr?url=<shortLink> or open them in the app.
- Store
id,key, andshortLinknext to your record. Print the image fromimageor from the/qrendpoint, or export SVG from the app. - To change where a code goes, send
PUT /qrcodes/{id}with{ "url": "https://example.com/menu-v2" }— see Change a QR code destination after printing. To convert a static record, send{ "static": false }. - To find a code from its printed short link, call
GET /qrcodes/info?domain=qrup.link&key=iVx0OCFyDn79-qr. To list,GET /qrcodes?type=wifi&pageSize=2returns the two most recent Wi-Fi codes;GET /qrcodes/countreturns the total (100in the test workspace). - To delete,
DELETE /qrcodes/{id}returns{ "id": "…" }; a scan of that code then answers302 https://codeqr.io/not-found.DELETE /qrcodes/bulk?qrcodeIds=id1,id2returns{ "deletedCount": 2 }— pass the ids comma-separated in one parameter, not repeated.
Other fields you can send: expiresAt + expiredUrl, password, folderId, tagIds, comments, trackConversion (Pro), rules (Smart Rules, Business, dynamic only), preRedirection + pageId (Business), and the design fields (fgColor, bgColor, size, pattern, shape, frame, frameText, frameTextStyles, showLogo). Types other than url take their object: wifi, whatsapp, vcard, email, phone, text, pix (static only) — examples in Create WhatsApp, Wi-Fi, and vCard QR codes.
Errors you will meet
- 422 — Message (verbatim):
invalid_type: type: Required· Fix: Sendtype - 422 — Message (verbatim):
custom: url: Invalid URL· Fix: Absolutehttps://URL - 400 — Message (verbatim):
Missing Wi-Fi SSID./Missing PIX data./Missing WhatsApp number.· Fix: Fill the type object - 400 — Message (verbatim):
Invalid size.· Fix:sizebetween 128 and 5000 - 422 — Message (verbatim):
invalid_string: whatsapp.number: Invalid WhatsApp number (use E.164 digits, e.g. 5511999999999)· Fix: Digits only, with country code - 422 — Message (verbatim):
too_big: frameText: Frame text must be at most 200 characters· Fix: ShortenframeText - 403 — Message (verbatim):
Smart rules can only be used on dynamic QR codes.· Fix: Droprulesor sendstatic: false - 403 — Message (verbatim):
You have reached the monthly limit of {N} qrcodes on the {Plan} plan. Please upgrade to add more QR Codes.· Fix: Wait for the next cycle, delete codes, or upgrade - 409 — Message (verbatim):
Duplicate key: this qr code already exists.· Fix: Use anotherkeyor omit it
Every error body has error.code, error.message, and error.doc_url.
The same through the MCP server
Connect your AI client to https://mcp.codeqr.io/mcp (OAuth 2.0). Ask, for example:
Create a QR code for https://example.com/menu titled "Cafe menu".
The client calls create_qrcode with { "url": "https://example.com/menu", "title": "Cafe menu" } and gets the created object with key and shortLink. Available tools: create_qrcode (types url, text, email, phone, sms, wifi, vcard, crypto, whatsapp; plus domain, key, size, level, fgColor, bgColor), update_qrcode (qrcodeId + the same payload fields, archived), list_qrcodes (page), delete_qrcode. The MCP tools create dynamic codes only and do not take static, design patterns, frames, rules, tags, folders, expiresAt, or password — use the API for those.
Automations
- Make: use the CodeQR app; the module Make an API Call sends any of the requests above (method
POST, URL/qrcodes, body as shown), which is how you create one code per spreadsheet row. Make also receives the QR code events below. - Webhooks: in Settings → Webhooks, subscribe to
qrcode.created,qrcode.updated,qrcode.deleted(workspace level) and, on Business,qrcode.scanned(chosen per code, with the scan's country, city, device, browser, and OS). Payloads: docs.codeqr.io. - Zapier: no QR code triggers or actions today (link and lead events only); use a webhook or HTTP request step with the API calls above.
Verify it works
curl -sI -A "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X)" \ https://qrup.link/XD2lytoBgPOe-qr | grep -iE "^HTTP|^location" # HTTP/2 302 # location: https://example.com/table/1
Then GET /qrcodes/info?domain=qrup.link&key=XD2lytoBgPOe-qr shows "scans": 1, and GET https://api.codeqr.io/analytics?event=clicks&groupBy=count&interval=24h&qrcodeId=cmsx899e5000hje43xlr42g24 returns {"clicks":0,"scans":1,"views":0,"leads":0,"sales":0,"saleAmount":0}. groupBy=top_qrcodes lists your codes with their scans.
Troubleshooting
The code I created through the API is static
static defaults to false, so codes are dynamic unless you send "static": true. Check the static field in the response.
GET /qrcodes/{id} returns 404
That route does not exist. Use GET /qrcodes/info?domain=…&key=… or GET /qrcodes?search=….
DELETE /qrcodes/bulk deleted only one code
Repeated qrcodeIds= parameters keep only one value. Send them comma-separated in a single qrcodeIds.
The API created a dynamic Pix code that opens the domain home page
Pix must be static: send "static": true with type: "pix".
DELETE /qrcodes/{id} returned 500
Check whether the code still exists with GET /qrcodes/info?domain=…&key=… before retrying: in our tests the code had been deleted even though the response was 500, and the retry answered 404 "qrCode not found.".
GET https://api.codeqr.io/qr?url=… returns 500
The url must be a CodeQR short link of an existing code (https://<domain>/<key>-qr); other URLs are not rendered.