REST API and OAuth
Manage dynamic QR codes, read scan analytics, report conversions and subscribe to webhooks over HTTP with OAuth 2.0 access tokens.
The QRdx REST API lives under https://api.qrdx.dev/v1. It uses JSON and, apart from the keyless image API, authenticates requests with an OAuth 2.0 access token:
curl https://api.qrdx.dev/v1/codes -H "Authorization: Bearer $QRDX_TOKEN"Requests made with a token act on the token owner's personal workspace. The codes, conversions and hooks endpoints also accept the browser session of someone signed in to QRdx; those requests act on the open team.
The same endpoints also answer under https://qrdx.dev/api/v1. Browser-session requests use that path, since the sign-in cookie belongs to qrdx.dev, and a self-hosted instance serves the API at /api/v1 on its own domain.
Endpoints
| Endpoint | Scope | What it does |
|---|---|---|
GET /v1/qr | None | QR code image as SVG, PNG or EPS. See Image API |
/v1/codes | codes:read, codes:write | List, create (one or up to 100 at a time), update and delete dynamic codes. See Dynamic QR codes |
GET /v1/codes/{id}/scans | codes:read | Scan analytics for a code, or individual scans page by page |
POST /v1/conversions | codes:write | Attribute a sale or sign-up to the scan that led to it (Business). See Tracking and conversions |
/v1/hooks | codes:read, codes:write | Subscribe a URL to events, REST Hooks style, for Zapier, Make and n8n. See Webhooks |
GET /v1/themes, GET /v1/themes/{id} | themes:read | Your saved QR themes (name and style) |
GET /v1/me | profile:read | The signed-in user's id, name, email and avatar |
codes:write also allows everything codes:read does.
Get an access token
QRdx is an OAuth 2.0 provider. Your app sends people to QRdx to sign in, and gets back an access token that can only do what the granted scopes allow.
| Scope | Allows |
|---|---|
profile:read | Read the user's name, email and avatar |
themes:read | Read the user's saved QR themes |
codes:read | Read dynamic codes and their scan analytics |
codes:write | Create, edit and delete dynamic codes, report conversions, manage webhook subscriptions |
Register an app
Create an OAuth app yourself in Settings → Developers: give it a name, its redirect URIs and the scopes it needs, and you get a client ID and a client secret. The secret is shown once; keep it on your server. Free workspaces can have 1 app, Pro 5 and Business 25. Build an integration walks through the whole flow, from creating the app to getting it listed in the integrations directory.
On a self-hosted instance you can also create a first-party app from apps/docs:
pnpm exec tsx scripts/create-oauth-app.ts \
--name "My app" \
--redirect-uris "https://myapp.example/callback" \
--scopes "codes:read,codes:write"Send the user to QRdx
https://qrdx.dev/api/oauth/authorize
?response_type=code
&client_id=<client id>
&redirect_uri=https://myapp.example/callback
&scope=codes:read codes:write
&state=<random value>
&code_challenge=<PKCE challenge>
&code_challenge_method=S256redirect_uri must exactly match a registered URL, and scope (space or comma separated) may only ask for the app's scopes. PKCE is recommended, and required for apps that turn on Require PKCE; S256 is the default method. People sign in if needed, then approve your app on the QRdx consent screen, which shows its name, logo, website, who built it and the requested permissions. QRdx then redirects to redirect_uri with code and your state, or with error=access_denied if they cancel. The code is valid for 10 minutes and can be used once.
Exchange the code for tokens
curl -X POST https://qrdx.dev/api/oauth/token \
-d grant_type=authorization_code \
-d client_id=$CLIENT_ID \
-d client_secret=$CLIENT_SECRET \
-d code=$CODE \
-d redirect_uri=https://myapp.example/callback \
-d code_verifier=$CODE_VERIFIER{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "...",
"scope": "codes:read codes:write"
}The token endpoint takes a form-encoded body. Access tokens last 1 hour and refresh tokens 30 days.
Refresh and revoke
# New access and refresh tokens; the old refresh token stops working
curl -X POST https://qrdx.dev/api/oauth/token \
-d grant_type=refresh_token \
-d client_id=$CLIENT_ID \
-d client_secret=$CLIENT_SECRET \
-d refresh_token=$REFRESH_TOKEN
# Revoke an access or refresh token
curl -X POST https://qrdx.dev/api/oauth/revoke -d token=$TOKENGET /api/oauth/userinfo returns the user as sub, name, email and picture for tokens with profile:read.
People can revoke an app's access themselves in Settings → Authorized apps. Its tokens for that account then fail with invalid_token (API calls) and invalid_grant (refresh), and the app has to ask again.
Errors and limits
The codes, conversions and hooks endpoints return errors as { "code": "...", "message": "...", "status": 400 }; the status codes are listed under Dynamic QR codes. The OAuth, themes and profile endpoints use the OAuth shape, { "error": "invalid_token", "error_description": "..." }.
Requests are rate limited, and a 429 response tells you when to retry. The codes API allows 120 reads and 30 writes a minute per user. Creating several codes in one request and moving codes onto a custom domain need Pro or Business (402 PLAN_REQUIRED otherwise). Scans are never limited.
Last updated on