QR for Agent Documentation
Everything you need to create, update, and track dynamic QR codes -- via MCP or REST API.
Quick Start
From zero to your first QR code in 30 seconds.
Install the MCP server
$ npx qr-for-agent Works with Claude Desktop, Cursor, and any MCP-compatible client.
Get your API key
Your key follows the format qr_ + 32-character random string. Keep it in your environment variables.
Create your first QR code
Ask your agent:
Create a dynamic QR code for https://example.com Or call the REST API directly:
$ curl -X POST https://api.qragentcore.com/api/qr \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY_HERE" \
-d '{"target_url": "https://example.com", "label": "My first QR"}' {
"id": 1,
"short_id": "wkQ5W-fm",
"short_url": "https://api.qragentcore.com/r/wkQ5W-fm",
"target_url": "https://example.com",
"label": "My first QR",
"format": "svg",
"image_data": "<svg>...</svg>",
"created_at": "2026-02-24T10:00:00.000Z"
} That's it. One command, one API call, one QR code. The short URL redirects to your target -- and you can change the target later without regenerating the image.
MCP Setup
QR for Agent ships as a standalone MCP server on npm. One package, 37 tools, 3 dependencies.
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"qr-for-agent": {
"command": "npx",
"args": ["-y", "qr-for-agent"],
"env": {
"API_KEY": "your-api-key",
"BASE_URL": "https://api.qragentcore.com"
}
}
}
} Restart Claude Desktop. Your agent now has 37 tools available.
Cursor
Add to .cursor/mcp.json in your project root:
{
"mcpServers": {
"qr-for-agent": {
"command": "npx",
"args": ["-y", "qr-for-agent"],
"env": {
"API_KEY": "your-api-key",
"BASE_URL": "https://api.qragentcore.com"
}
}
}
} Reload the window. The tools appear in Cursor's MCP panel.
Any MCP client
The server runs via npx qr-for-agent and communicates over stdio. Any client that supports the MCP standard can use it. Set API_KEY and BASE_URL as environment variables.
API Reference
All /api/* endpoints require an X-API-Key header. Public endpoints (/r/*, /i/*) don't require auth.
QR Code Endpoints
| Method | Path | Description | Auth |
|---|---|---|---|
| POST | /api/qr | Create a new dynamic QR code | Yes |
| GET | /api/qr | List all QR codes (paginated) | Yes |
| GET | /api/qr/:shortId | Get QR code details | Yes |
| PATCH | /api/qr/:shortId | Update target URL or label | Yes |
| DELETE | /api/qr/:shortId | Delete QR code and analytics | Yes |
| GET | /api/qr/:shortId/image | Download QR image | Yes |
| POST | /api/qr/bulk | Create up to 50 QR codes at once | Yes |
| PATCH | /api/qr/bulk | Update up to 50 QR codes at once | Yes |
| DELETE | /api/qr/bulk | Delete up to 50 QR codes at once | Yes |
Analytics Endpoint
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /api/analytics/:shortId | Scan count and recent events | Yes |
Webhook Endpoints
| Method | Path | Description | Auth |
|---|---|---|---|
| POST | /api/webhooks | Register a webhook endpoint | Yes |
| GET | /api/webhooks | List all webhooks | Yes |
| DELETE | /api/webhooks/:id | Delete a webhook | Yes |
Account Endpoints
| Method | Path | Description | Auth |
|---|---|---|---|
| POST | /api/register | Register for an API key | No |
| GET | /api/usage | Get current usage and quota | Yes |
| POST | /api/stripe/checkout | Create Stripe Checkout session for Pro upgrade | Yes |
| POST | /api/stripe/portal | Open Stripe billing portal | Yes |
Public Endpoints
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /r/:shortId | Redirect to target URL (records scan) | No |
| GET | /i/:shortId | Serve QR image (cacheable) | No |
| GET | /health | Health check | No |
| GET | /documentation | Swagger UI (OpenAPI docs) | No |
POST /api/qr -- Create a QR code
Request body:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| target_url | string | Yes | -- | The destination URL. Must be a fully-qualified absolute URL. |
| label | string | No | null | A human-readable label for organizing QR codes. |
| format | string | No | "svg" | Image format: "svg" (recommended) or "png". |
| foreground_color | string | No | "#000000" | Hex color for QR code dots. |
| background_color | string | No | "#ffffff" | Hex color for QR code background. |
| dot_style | string | No | "square" | "square", "rounded", "dots", or "classy-rounded". |
| corner_style | string | No | "square" | "square", "extra-rounded", or "dot". |
| logo_url | string | No | -- | URL to a logo image (PNG/JPG/SVG) or data: URI. Centered on the QR code. |
| logo_size | number | No | 0.2 | Logo size as ratio of QR width (0.15-0.3). |
| width | integer | No | 400 | QR code width in pixels (200-2000). |
| margin | integer | No | 2 | Quiet zone margin in modules (0-10). |
| error_correction | string | No | "M" | "L" (7%), "M" (15%), "Q" (25%), "H" (30%). Auto-set to H with logo. |
| expires_at | string | No | -- | ISO 8601 date-time. After this date, scanning returns 410 Gone instead of redirecting. |
| scheduled_url | string | No | -- | Replacement URL that activates at scheduled_at. |
| scheduled_at | string | No | -- | ISO 8601 date-time. When reached, target automatically switches to scheduled_url. |
Basic request:
$ curl -X POST https://api.qragentcore.com/api/qr \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY" \
-d '{
"target_url": "https://conf.example.com/2026",
"label": "Conference landing page",
"format": "svg"
}' Styled request (colors, rounded dots, logo):
$ curl -X POST https://api.qragentcore.com/api/qr \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY" \
-d '{
"target_url": "https://conf.example.com/2026",
"label": "Branded QR",
"foreground_color": "#4F46E5",
"background_color": "#F9FAFB",
"dot_style": "rounded",
"corner_style": "extra-rounded",
"logo_url": "https://example.com/logo.png",
"logo_size": 0.25
}' Response (201):
{
"id": 1,
"short_id": "wkQ5W-fm",
"short_url": "https://api.qragentcore.com/r/wkQ5W-fm",
"target_url": "https://conf.example.com/2026",
"label": "Conference landing page",
"format": "svg",
"image_data": "<svg>...</svg>",
"created_at": "2026-02-24T10:00:00.000Z"
} QR Types
QR for Agent supports 11 types of QR codes: URL, vCard, WiFi, Email, SMS, Phone, Event/Calendar, Text, Location, Social Media, and App Store. Each type determines how the /r/:shortId endpoint behaves when scanned.
| Type | Redirect Behavior | Use Case |
|---|---|---|
| url | 302 redirect to target URL | Standard dynamic links (default) |
| vcard | Serves .vcf file (vCard 3.0 format) | Digital business cards, contact info |
| wifi | Returns WiFi network credentials | WiFi network sharing |
| Opens mail client with pre-filled email | Contact forms, support requests | |
| sms | Opens SMS app with pre-filled message | Marketing campaigns, quick responses |
| phone | Initiates phone call | Customer support, contact buttons |
| event | Opens calendar with event details | Event invitations, appointments |
| text | Displays plain text content | Instructions, codes, messages |
| location | Opens map app with coordinates | Directions, venue locations |
| social | Landing page with social media links | Link-in-bio, social profiles |
| app_store | Smart redirect to iOS/Android store | App downloads, store links |
Create a vCard QR code:
$ curl -X POST https://api.qragentcore.com/api/qr \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY" \
-d '{
"type": "vcard",
"target_url": "John Doe|Acme Corp|CEO|+1234567890|[email protected]|https://acme.com",
"label": "John Doe Business Card",
"format": "svg"
}'
The target_url field for vCard should contain pipe-separated values: Name|Organization|Title|Phone|Email|Website. When scanned, the QR code serves a .vcf file that opens in the phone's contact app.
Create a WiFi QR code:
$ curl -X POST https://api.qragentcore.com/api/qr \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY" \
-d '{
"type": "wifi",
"target_url": "MyNetwork|WPA|mypassword123|false",
"label": "Office WiFi",
"format": "svg"
}'
The target_url field for WiFi should contain pipe-separated values: SSID|Security|Password|Hidden. Security can be "WPA", "WEP", or "nopass". Hidden is "true" or "false". When scanned, the QR code returns JSON with WiFi credentials that the phone's OS can use to auto-join the network.
PATCH /api/qr/:shortId -- Update destination
The QR image stays the same. Only the redirect target changes. This is the core dynamic link feature.
$ curl -X PATCH https://api.qragentcore.com/api/qr/wkQ5W-fm \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY" \
-d '{"target_url": "https://new-destination.com"}' Expiration & Scheduled URL Swap
Set an expiration date to automatically disable a QR code, or schedule a URL swap to change where it points at a future date. Both are checked at scan time -- no cron jobs needed.
Create a QR code that expires:
$ curl -X POST https://api.qragentcore.com/api/qr \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY" \
-d '{
"target_url": "https://example.com/flash-sale",
"label": "Flash sale - 48h only",
"expires_at": "2026-04-01T00:00:00.000Z"
}'
After expires_at, scanning returns 410 Gone with a QR_EXPIRED code. Remove the expiration anytime via PATCH (expires_at: null).
Schedule a URL swap:
$ curl -X POST https://api.qragentcore.com/api/qr \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY" \
-d '{
"target_url": "https://example.com/pre-launch",
"scheduled_url": "https://example.com/product-page",
"scheduled_at": "2026-04-15T09:00:00.000Z",
"label": "Product launch QR"
}'
Before the date, scans go to target_url. After the date, the first scan triggers the swap -- target_url becomes scheduled_url permanently. Cancel anytime via PATCH (scheduled_url: null, scheduled_at: null).
GET /api/qr -- List QR codes
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 20 | Max results (1-100). |
| offset | integer | 0 | Number of records to skip. |
$ curl https://api.qragentcore.com/api/qr?limit=10&offset=0 \
-H "X-API-Key: qr_YOUR_KEY" GET /api/analytics/:shortId -- Enriched scan analytics
Returns scan statistics with aggregations by device, browser, country, referer, and daily trends. Supports ?period=7d|30d|90d|all (default: 30d). User-agent is parsed and IPs are geolocated at scan time.
$ curl https://api.qragentcore.com/api/analytics/wkQ5W-fm?period=30d \
-H "X-API-Key: qr_YOUR_KEY" Response:
{
"short_id": "wkQ5W-fm",
"total_scans": 142,
"period": "30d",
"scans_by_day": [
{ "date": "2026-02-26", "count": 12 },
{ "date": "2026-02-27", "count": 8 }
],
"top_devices": [
{ "device_type": "mobile", "count": 89, "percentage": 62.7 },
{ "device_type": "desktop", "count": 45, "percentage": 31.7 }
],
"top_browsers": [
{ "browser": "Chrome", "count": 65, "percentage": 45.8 },
{ "browser": "Safari", "count": 42, "percentage": 29.6 }
],
"top_countries": [
{ "country": "FR", "count": 80, "percentage": 56.3 },
{ "country": "US", "count": 30, "percentage": 21.1 }
],
"top_referers": [
{ "referer": "instagram.com", "count": 30, "percentage": 21.1 },
{ "referer": "(direct)", "count": 25, "percentage": 17.6 }
],
"recent_scans": [
{
"scanned_at": "2026-02-27T14:32:00.000Z",
"user_agent": "Mozilla/5.0 ...",
"referer": "https://instagram.com/",
"device_type": "mobile",
"browser": "Safari",
"os": "iOS",
"country": "FR",
"city": "Paris"
}
]
} DELETE /api/qr/:shortId -- Delete a QR code
Permanently removes the QR code and all associated scan analytics. The short URL stops working immediately. This cannot be undone.
$ curl -X DELETE https://api.qragentcore.com/api/qr/wkQ5W-fm \
-H "X-API-Key: qr_YOUR_KEY" POST /api/webhooks -- Register a webhook
Register an endpoint to receive real-time POST notifications when your QR codes are scanned. Returns an HMAC-SHA256 secret for signature verification -- store it securely, it is only shown once.
Request body:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| url | string | Yes | -- | The HTTP/HTTPS endpoint that will receive webhook events. |
| events | string[] | No | ["qr.scanned"] | Events to subscribe to: "qr.scanned", "qr.conversion". |
$ curl -X POST https://api.qragentcore.com/api/webhooks \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY" \
-d '{"url": "https://your-server.com/webhook"}' Response (201):
{
"id": 1,
"url": "https://your-server.com/webhook",
"secret": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"events": ["qr.scanned"],
"is_active": true,
"created_at": "2026-02-27T10:00:00.000Z"
} Webhook payload (sent to your endpoint):
{
"event": "qr.scanned",
"timestamp": "2026-02-27T14:32:00.000Z",
"data": {
"short_id": "wkQ5W-fm",
"target_url": "https://example.com",
"label": "My QR",
"scan": {
"user_agent": "Mozilla/5.0 ...",
"referer": null,
"ip": "203.0.113.42",
"scanned_at": "2026-02-27T14:32:00.000Z"
}
}
} Verifying signatures:
Every webhook request includes an X-Webhook-Signature header containing an HMAC-SHA256 hex digest of the request body, signed with your webhook secret.
import crypto from "node:crypto";
function verifySignature(body, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
return signature === expected;
} GET /t/:shortId -- Tracking pixel (conversion)
Zero-JavaScript conversion tracking. Embed this URL as an invisible image on your thank-you page. Returns a 1×1 transparent GIF. No authentication required -- the short ID scopes the event to your QR code.
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| event | string | Yes | Event name (e.g. "purchase", "signup", "lead"). |
| value | number | No | Monetary value of the conversion. |
<img src="https://api.qragentcore.com/t/wkQ5W-fm?event=purchase&value=49.99" width="1" height="1" alt="" /> POST /api/conversions -- Record a conversion
Record a conversion event programmatically. Use this when server-side tracking is preferred over the pixel approach.
$ curl -X POST https://api.qragentcore.com/api/conversions \
-H "Content-Type: application/json" \
-H "X-API-Key: qr_YOUR_KEY" \
-d '{"short_id": "wkQ5W-fm", "event": "purchase", "value": 49.99}' Response (201):
{
"id": 1,
"qr_code_id": 42,
"event_name": "purchase",
"value": "49.99",
"created_at": "2026-03-02T10:00:00.000Z"
} GET /api/conversions/:shortId -- Conversion stats
Get conversion analytics for a QR code: totals, breakdowns by event name, daily trends, and recent events. Supports period filtering.
$ curl https://api.qragentcore.com/api/conversions/wkQ5W-fm?period=30d \
-H "X-API-Key: qr_YOUR_KEY" Response (200):
{
"short_id": "wkQ5W-fm",
"total_conversions": 42,
"total_value": 1250.50,
"period": "30d",
"by_event": [
{ "event_name": "purchase", "count": 30, "total_value": 1200.00 },
{ "event_name": "signup", "count": 12, "total_value": 50.50 }
],
"by_day": [
{ "date": "2026-03-01", "count": 5, "total_value": 200.00 }
],
"recent_events": [
{
"event_name": "purchase",
"value": "49.99",
"metadata": {},
"created_at": "2026-03-02T10:00:00.000Z"
}
]
} Authentication
All /api/* endpoints require an X-API-Key header.
- Format:
qr_+ 32-character random string - Multi-tenant: each API key only sees its own QR codes
- Isolation: no data leaks between API keys -- build agents for multiple clients safely
MCP Tools
37 tools. No context bloat, no "too many tools" problem. Each tool maps 1:1 to a REST endpoint.
QR Code Tools
| Tool | Key Parameters | Description |
|---|---|---|
| create_qr_code | target_url, format, dot_style, corner_style, foreground_color, logo_url, ... | Create a custom-styled dynamic QR code. |
| get_qr_code | short_id | Retrieve details and metadata for an existing QR code. |
| update_qr_destination | short_id, target_url, label | Change redirect target without regenerating the image. |
| list_qr_codes | limit, offset | List all QR codes with pagination. |
| delete_qr_code | short_id | Delete a QR code and its scan analytics. |
| get_qr_analytics | short_id | Get scan count and recent scan events. |
| create_vcard_qr | name, email, phone, organization, ... | Create a contact card QR code with name, email, phone, organization, etc. |
| create_wifi_qr | ssid, password, encryption | Create a WiFi QR code with SSID, password, and encryption type. |
| create_email_qr | email, subject, body | Create a pre-filled email QR code. |
| create_sms_qr | phone, message | Create a pre-filled SMS QR code. |
| create_phone_qr | phone | Create a phone call QR code. |
| create_event_qr | title, start, end, location, ... | Create a calendar event QR code. |
| create_text_qr | text | Create a plain text QR code. |
| create_location_qr | latitude, longitude, name | Create a map location QR code. |
| create_social_qr | title, links[] | Create a social media links QR code. |
| create_app_store_qr | ios_url, android_url | Create a smart app store redirect QR code. |
| update_vcard_qr | short_id, name, email, phone, ... | Update contact fields on a vCard QR code (partial merge). |
| update_wifi_qr | short_id, ssid, password, ... | Update WiFi credentials on a WiFi QR code (partial merge). |
| update_social_qr | short_id, links[] | Update social media links. |
| update_app_store_qr | short_id, ios_url, android_url | Update app store URLs. |
| bulk_create_qr_codes | items[] (target_url, label, format, style...) | Create up to 50 QR codes in one request. |
| bulk_update_qr_codes | items[] (short_id, target_url, label) | Update up to 50 QR codes in one request. |
| bulk_delete_qr_codes | short_ids[] | Delete up to 50 QR codes in one request. |
| bulk_create_from_csv | csv_content | Create up to 500 QR codes from CSV data. Pro only. |
| set_utm_params | short_id, utm_source, utm_medium, utm_campaign, ... | Set UTM tracking parameters on a URL QR code. |
| set_redirect_rules | short_id, rules[] | Set conditional redirect rules on a URL QR code (device, OS, country, language, time, A/B). |
Webhook Tools
| Tool | Key Parameters | Description |
|---|---|---|
| create_webhook | url, events | Register an endpoint for real-time scan notifications (HMAC-signed). |
| list_webhooks | -- | List all registered webhook endpoints. |
| delete_webhook | webhook_id | Remove a webhook endpoint. |
Conversion Tools
| Tool | Key Parameters | Description |
|---|---|---|
| record_conversion | short_id, event, value | Record a conversion event (purchase, signup) for a QR code. |
| get_conversions | short_id, period | Get conversion stats: totals, by event, by day, recent events. |
Domain Tools
| Tool | Key Parameters | Description |
|---|---|---|
| set_custom_domain | domain | Set a custom domain for branded short URLs. Pro only. |
| get_custom_domain | -- | Get current custom domain and DNS verification status. |
Account Tools
| Tool | Key Parameters | Description |
|---|---|---|
| register | email, label | Register for an API key with your email. |
| get_usage | -- | Get current usage and quota for your API key. |
| upgrade_to_pro | -- | Upgrade to Pro ($19/month) -- returns a Stripe Checkout URL. |
| manage_billing | -- | Open Stripe billing portal to manage subscription. |
Tool details
create_qr_code
Creates a custom-styled QR code with full control over appearance: colors, dot shapes (square, rounded, dots, classy-rounded), corner shapes (square, extra-rounded, dot), and optional logo embedding. The QR code points to a short URL that redirects to your target. Change the target later without regenerating the image.
update_qr_destination
This is the key "dynamic link" feature: the QR image stays the same, but scanning it goes to the new URL. Use it to update campaigns, fix broken links, or A/B test landing pages.
create_webhook
Register a webhook endpoint to get real-time notifications when any of your QR codes is scanned. Each webhook delivery is signed with HMAC-SHA256 for verification. The secret is only returned at creation time.
get_qr_analytics
Returns enriched scan analytics with breakdowns by device, browser, country, referer, and daily trends. Supports a period parameter (7d, 30d, 90d, all). User-agents are parsed and IPs geolocated at scan time — your agent gets structured data, not raw strings.
record_conversion
Track post-scan events like purchases, signups, or leads. Pair with the zero-JS tracking pixel (GET /t/:shortId?event=purchase&value=49.99) for server-free conversion tracking, or call the API directly from your backend.
bulk_create_from_csv
Create up to 500 QR codes from CSV data in a single call. Pro only. Each row maps to a QR code with support for all style options, types, and frames. Row-level validation with error line numbers.
Environment Variables
MCP Server (qr-for-agent)
| Variable | Required | Default | Description |
|---|---|---|---|
| API_KEY | Yes | -- | Your QR for Agent API key. Format: qr_ + 32 chars. |
| BASE_URL | No | http://localhost:3100 | The URL of your QR for Agent instance. Use https://api.qragentcore.com for the hosted service. |
Self-Hosted Server
| Variable | Required | Default | Description |
|---|---|---|---|
| PORT | No | 3100 | HTTP port. |
| HOST | No | 0.0.0.0 | Bind address. |
| BASE_URL | No | http://localhost:3100 | Public URL used in generated short URLs. Set this to your domain. |
| DATABASE_URL | No | ./data/qr-agent.db | SQLite file path. |
| SHORT_ID_LENGTH | No | 8 | Length of generated short IDs. |
Self-Hosting
QR for Agent is open source and MIT-licensed. Run it on your own infrastructure or use the hosted service. Your call.
Docker
$ git clone https://github.com/benswel/qr-agent-core.git
$ cd qr-agent-core
$ docker compose up -d
The database is persisted in a Docker volume. Your server runs at http://localhost:3100.
On first startup, an API key is auto-generated and printed to the console. Use docker compose logs to retrieve it.
docker-compose.yml:
services:
qr-agent-core:
build: .
ports:
- "3100:3100"
volumes:
- qr-data:/app/data
environment:
- PORT=3100
- HOST=0.0.0.0
- BASE_URL=https://your-domain.com
- DATABASE_URL=/app/data/qr-agent.db
restart: unless-stopped
volumes:
qr-data: Railway
The project includes railway.toml and a multi-stage Dockerfile. Deploy in three steps:
- Fork the repo on GitHub
- Connect it to Railway
- Railway builds and deploys automatically with health checks on
/health
Set BASE_URL to your Railway domain in the environment variables.
Without Docker
$ git clone https://github.com/benswel/qr-agent-core.git
$ cd qr-agent-core
$ npm install
$ npm run dev On first startup, an API key is auto-generated and printed to the console.
Managing API keys:
$ npm run key:create "my-label" # Create a new API key
$ npm run key:list # List all API keys Teams keep rebuilding small but critical utilities -- QR codes, short URLs, tokens -- over and over again. We built it once, correctly, and opened the source.
View on GitHubStart building
Free tier. Full API access. No credit card.
$ npx qr-for-agent Questions? Open an issue on GitHub or check the OpenAPI docs (Swagger UI).