QRQR Code Agency
Getting started

Authentication

How to authenticate against the QR Code Agency API. API keys, rotation, revocation, best practices.

Authentication

Every call to /api/v1/generate/, /api/v1/generate/bulk/, and /api/v1/usage/ requires an API key in the X-Api-Key header. There is no OAuth, no JWT, no cookie. One key, one header.

The dashboard endpoints (key management, dynamic QR CRUD, webhooks, billing) use a JWT bearer token instead. You will not need that for a machine-to-machine integration.

API key format

smk_aBc12dEf3GhI4jKlMnOpQrStUvWxYz67890
  • Always starts with smk_
  • 36 URL-safe characters after the prefix
  • Alphabet is letters, digits, -, _

danger: Treat keys like passwords A key is bearer-only. Anyone holding it can spend your quota and incur overage charges. Never commit a key to git, never embed in client-side JavaScript, never paste in a screenshot for support. We rotate the moment a leak is suspected.

Where to put the key

In every request, set the X-Api-Key header:

cURL

curl -H "X-Api-Key: smk_yOuRkEy" https://api.qrstudio.agency/api/v1/usage/

Python

headers = {"X-Api-Key": "smk_yOuRkEy"}
requests.post(url, headers=headers, json=payload)

JavaScript

fetch(url, {
method: "POST",
headers: {
"X-Api-Key": "smk_yOuRkEy",
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});

C#

httpClient.DefaultRequestHeaders.Add("X-Api-Key", "smk_yOuRkEy");

Go

req.Header.Set("X-Api-Key", "smk_yOuRkEy")

Create a key

Two paths depending on who you are.

A. From the dashboard (preferred)

  1. Sign in at qrstudio.agency with your account
  2. Go to Settings -> API keys -> Create key
  3. Pick a name (prod-billing-renderer, staging, marketing-campaign)
  4. Copy the raw key shown once. Paste it into your secrets manager.

B. Programmatically (server-to-server)

If you are authenticated as a Django user holding a JWT, POST to /api/v1/keys/:

curl -X POST https://api.qrstudio.agency/api/v1/keys/ \
 -H "Authorization: Bearer <your-jwt>" \
 -H "Content-Type: application/json" \
 -d '{"name": "production-server-1", "plan": "starter"}'

Response:

{
 "id": 7,
 "name": "production-server-1",
 "raw_key": "smk_aBc12dEf3GhI4jKlMnOpQrStUvWxYz67890",
 "key_prefix": "smk_aBc12dEf",
 "plan": "starter",
 "warning": "Store this key securely. It will NOT be shown again."
}

important: One-time disclosure The raw key appears only at creation. We store a SHA-256 hash and cannot recover the original. If you lose it, revoke the old one and create a new one.

List, rotate, and revoke keys

ActionEndpointResult
List your keysGET /api/v1/keys/Array of metadata. Never the raw key.
Rotate a keyPOST /api/v1/keys/<id>/rotate/Issues a new raw key, old key invalidated immediately.
Revoke a keyDELETE /api/v1/keys/<id>/Permanently rejects further calls with 401.

See the full reference in Keys management.

Best practices

tip: Rotate every 90 days Create the new key, deploy it, deploy verification, revoke the old one. Use the rotate endpoint to keep the same key id and audit trail.

tip: One key per environment Do not share keys between dev, staging, and prod. If staging gets compromised, you do not want production exposed.

tip: One key per integration Issue separate keys for your website, mobile app, marketing tool, and internal jobs. The audit log shows which key did what, narrower keys make incidents easier to scope.

tip: Store keys in a secrets manager AWS Secrets Manager, Google Secret Manager, HashiCorp Vault, Doppler, 1Password Secrets Automation, GitHub Actions secrets, Vercel Environment Variables. Do not hardcode in source.

Calling from the browser

Direct browser calls expose your key in DevTools. Always proxy through your own backend:

sequenceDiagram
 participant B as Browser
 participant Y as Your backend
 participant Q as QR Code Agency

 B->>Y: POST /generate-qr<br/>(no API key, just a session cookie)
 Y->>Y: Authenticate user
 Y->>Q: POST /api/v1/generate/<br/>X-Api-Key: smk_...
 Q-->>Y: 200 OK + image bytes
 Y-->>B: 200 OK + image bytes

Your backend authenticates the human (cookie, session, JWT), validates the request, then calls QR Code Agency with the secret key on its own.

warning: CORS is intentionally restrictive The API does not send Access-Control-Allow-Origin: *. Browser requests are blocked by design. This is not a bug.

How auth resolves on the server

For curiosity, the server tries authentication classes in order until one succeeds:

  1. X-Api-Key header -> resolved by SHA-256 hash lookup against QrApiKey. Returns (owner, key) or falls through.
  2. Authorization: Bearer <jwt> -> SimpleJWT validates the access token. Returns the user or falls through.
  3. Session cookie -> Django session middleware. Used by the dashboard exclusively.

Each step returns None on miss to fall through to the next, so a request carrying both an API key and a session cookie is authenticated as the API key owner.

Common errors

StatusWhyFix
401Header missing or wrong formatInclude X-Api-Key: smk_...
401Key revoked or rotatedUse the new key or create one
401Hash mismatch (typo, copy paste error)Verify against the dashboard
403Plan does not allow what you askedUpgrade plan or reduce request
429Monthly quota hit on Free/EnterpriseUpgrade or wait for the 1st

For a deeper troubleshooting trail, see Errors.

On this page