API Documentation
Quick Start
Get your API key
Sign up or log in to generate your API key from the dashboard.
Make your first request
Use the API key to authenticate and create your first QR code.
Integrate and scale
Build powerful QR code features into your applications.
Include your API key in the x-api-key header:
curl -H "x-api-key: YOUR_API_KEY" https://uqr.ai/api/v1/qr-codesOr use the Authorization header with Bearer token:
curl -H "Authorization: Bearer YOUR_API_KEY" https://uqr.ai/api/v1/qr-codesMCP is for an assistant in a chat window. REST is for code you write. They share the same codes. They do not share the same auth.
Hosted HTTP at https://uqr.ai/mcp. Streamable HTTP. No local process, no npx, no stdio. That URL is the server, not a documentation page — this section is the docs.
Plan
Pro and above. Free and Lite get 403. Same cut as the REST API. Bulk CSV stays on Business.
A code the assistant creates is an ordinary dynamic code: editable in the dashboard, tracked, never deactivated. Unlimited scans. No ads on the scan path. If you stop paying, you downgrade. Existing codes keep resolving.
Connect with OAuth
MCP clients (Claude, Cursor, ChatGPT in developer mode) use OAuth 2.1 (authorization code + PKCE) at https://uqr.ai/oauth/authorize.
The consent screen picks:
| Scope | What it allows |
|---|---|
mcp:read | analytics |
mcp:write | create + repoint + update_link_page |
Write is never granted unless you opt in. After connect, those are the capabilities. There is no bulk, folders, or delete.
Create a page with MCP create and type_id: 20. Edit it with update_link_page using its id or short_id. Both require mcp:write. Link page customization
Cursor
The Cursor marketplace plugin is not live yet. Until it is, add the server by URL: https://uqr.ai/mcp.
Scripts still use API keys
Automations and your own code keep using a Pro or Business API key (x-api-key or Authorization: Bearer uqr_…) against the REST API above. A key is full access, not a scoped MCP grant. Do not put a key in the MCP OAuth consent.
Related
GET/qr-codesList all QR codes
POST/qr-codesCreate a new QR code
GET/qr-codes/:idGet a single QR code
PUT/qr-codes/:idUpdate a QR code
DELETE/qr-codes/:idDelete a QR code
GET/linksList short links
POST/linksCreate a short link
GET/links/:idGet a single short link
PUT/links/:idUpdate a short link
DELETE/links/:idDelete a short link
GET/contactsList lead-capture form submissions (contacts), newest first. Beyond your plan's monthly limit, the newest submissions are locked (see meta.locked) until you upgrade.
Full Options Schema
{
"options": {
"width": 1080,
"height": 1080,
"margin": 0,
"image": "https://example.com/logo.png",
"qrOptions": {
"errorCorrectionLevel": "Q" // L, M, Q, H
},
"dotsOptions": {
"type": "square",
"color": "#000000",
"gradient": null
},
"backgroundOptions": {
"color": "#ffffff"
},
"cornersSquareOptions": {
"type": "square",
"color": "#000000"
},
"cornersDotOptions": {
"type": "square",
"color": "#000000"
},
"imageOptions": {
"hideBackgroundDots": true,
"imageSize": 0.4,
"margin": 0
},
"frameOptions": {
"enabled": false,
"type": "bottom",
"text": "Scan Me",
"backgroundColor": "#ffffff",
"textColor": "#000000",
"fontSize": 16
}
}
}Design Examples
{
"dotsOptions": { "type": "rounded", "color": "#1e40af" },
"cornersSquareOptions": { "type": "extra-rounded", "color": "#1e3a8a" },
"backgroundOptions": { "color": "#eff6ff" }
}{
"dotsOptions": {
"type": "dots",
"gradient": {
"colorStops": [
{ "offset": 0, "color": "#ec4899" },
{ "offset": 1, "color": "#8b5cf6" }
]
}
},
"cornersSquareOptions": { "type": "dot", "color": "#8b5cf6" }
}{
"dotsOptions": { "type": "rounded", "color": "#000000" },
"frameOptions": {
"enabled": true,
"type": "bottom",
"text": "SCAN ME",
"backgroundColor": "#f1f5f9",
"textColor": "#334155",
"fontSize": 18
}
}{
"image": "https://example.com/logo.png",
"dotsOptions": { "type": "rounded", "color": "#000000" },
"imageOptions": {
"hideBackgroundDots": true,
"imageSize": 0.3,
"margin": 5
},
"qrOptions": { "errorCorrectionLevel": "H" }
}Dot Styles
Corner Styles
| Field | DB Type | Required | Description |
|---|---|---|---|
type_id | BIGINT | QR type identifier (1-24) | |
name | TEXT | - | QR code display name |
short_link_domain | TEXT | - | Domain for the short link (e.g. uqr.page). Defaults to system default if not specified |
options | JSONB | - | Design options object |
folder | UUID | - | Folder ID for organization |
template_id | UUID | - | Design template ID |
theme | JSONB | - | Theme configuration for landing pages |
| Field | DB Type | Description |
|---|---|---|
short_id | TEXT | Short ID for URL |
short_link_domain | TEXT | Domain used for the short link (e.g. uqr.page) |
short_url | N/A | Full scannable URL |
image | TEXT | URL to generated QR code image |
image_status | N/A | Image processing status |
image_base64 | N/A | Base64 encoded image (initial only) |
1Website/URL1 required
2Text1 required
3Email1 required
4vCard0 required
5WiFi1 required
6Phone1 required
12SMS1 required
13Location2 required
14PDF3 required
15Image Gallery2 required
17Image2 required
19File2 required
20List of Links0 required
21MP32 required
24Product1 required
Link colors inherit from theme. Per-link appearance overrides are independent; a custom gradient takes precedence over a solid background.
| Field | Description |
|---|---|
| links_list[].iconMode | favicon | image | icon |
| links_list[].iconImage | { url, filename?, originalFilename?, contentType? } |
| links_list[].appearance.backgroundColor | #RGB | #RRGGBB |
| links_list[].appearance.textColor | #RGB | #RRGGBB |
| links_list[].appearance.backgroundGradient | { rotation: 0–360, colorStops: [{ offset: 0, color }, { offset: 1, color }] } |
| links_list[].appearance.animation | none | pulse | bounce | wiggle | float | glow |
| theme.buttonGradient | linear-gradient(135deg, #243b2e 0%, #416a43 100%) |
| theme.buttonTextColor | #RGB | #RRGGBB |
| theme.profileImageShape | circle | rounded | original |
| theme.profileImageCrop | { x: 0–100, y: 0–100, zoom: 1–3 } | null |
| theme.backgroundPosition | top left | top | top right | left | center | right | bottom left | bottom | bottom right |
| theme.backgroundSize | cover | contain | auto |
| theme.backgroundRepeat | no-repeat | repeat | repeat-x | repeat-y |
Custom icons use hosted image URLs. GIF animation is preserved. These JSON requests do not upload files or accept local draft fields.
Choose none, pulse, bounce, wiggle, float or glow. Animations finish after 4.8 seconds and respect reduced motion.
Background images support GIF and WebP animation. Set theme.backgroundPosition, theme.backgroundSize and theme.backgroundRepeat; defaults are center, cover and no-repeat.
Choose a free, self-hosted page font with theme.fontFamily. Read and merge the complete current theme before updating it. theme.fontFamily: default (Inter), dm-sans, outfit, nunito, lora, space-mono.
Set theme.profileImageShape to circle, rounded or original. For avatars, theme.profileImageCrop accepts x/y anchors from 0–100 (default 50) and zoom from 1–3 (default 1). Null resets framing. Original keeps logos uncropped; the source image and GIF animation are preserved.
Updates preserve omitted fields. A supplied links_list or theme replaces its whole value: read it first, merge your changes, then save. Remove an appearance field to restore its default.
Create a page with MCP create and type_id: 20. Edit it with update_link_page using its id or short_id. Both require mcp:write.
{
"type_id": 20,
"name": "Studio link page",
"links_title": "My studio",
"links_description": "Work, updates and ways to get in touch.",
"links_logo_image_url": "https://cdn.example.com/studio-logo.png",
"links_header_image_url": "https://cdn.example.com/studio-background.gif",
"links_list": [
{
"title": "Portfolio",
"url": "https://example.com/work",
"iconMode": "favicon"
},
{
"title": "Latest release",
"url": "https://example.com/release",
"iconMode": "image",
"iconImage": {
"url": "https://cdn.example.com/release.gif",
"contentType": "image/gif"
},
"appearance": {
"textColor": "#ffffff",
"backgroundGradient": {
"rotation": 135,
"colorStops": [
{
"offset": 0,
"color": "#7c3aed"
},
{
"offset": 1,
"color": "#ec4899"
}
]
},
"animation": "pulse"
}
},
{
"title": "Contact",
"url": "mailto:[email protected]",
"iconMode": "icon",
"icon": "mail"
}
],
"links_social_links": [
{
"icon": "instagram",
"url": "https://instagram.com/example"
}
],
"links_social_position": "bottom",
"theme": {
"fontFamily": "lora",
"backgroundColor": "#f7f7f2",
"profileImageShape": "circle",
"profileImageCrop": {
"x": 50,
"y": 40,
"zoom": 1.2
},
"backgroundPosition": "top right",
"backgroundSize": "contain",
"backgroundRepeat": "repeat-y",
"textColor": "#20251e",
"buttonColor": "#243b2e",
"buttonGradient": "linear-gradient(135deg, #243b2e 0%, #416a43 100%)",
"buttonTextColor": "#ffffff"
}
}missing_api_keyNo API key provided in the request
invalid_api_keyThe provided API key is invalid
api_key_inactiveThe API key has been deactivated
api_plan_requiredThe REST API requires a Pro plan or higher
premium_requiredThe requested feature requires a pro subscription
plan_limit_reachedPlan quota reached — upgrade to create more
validation_errorInvalid request parameters
not_foundThe requested resource was not found
database_errorA database error occurred
server_errorAn unexpected server error occurred