Skip to content

QR codes ​

A QR code belongs to exactly one campaign. The printed code always encodes a redirect on this API, which looks up the current destination and forwards the reader there — so url can be changed after the mail is posted. trackingEnabled additionally records each scan. See QR codes for the model.

Attach before printing starts

The QR image is rendered into the PDF as the campaign is processed. Attaching a code to a campaign that has already reached completed will not change what was printed. The reliable approach is the qrCode field on POST /api/v1/campaigns, which persists the code inside the same transaction as the campaign.

Attach a QR code to a campaign ​

POST/api/v1/qr-codes
requires qr-codes:create

Requires qr-codes:create. A campaign may have at most one QR code.

Body ​

FieldTypeRequiredNotes
campaignIduuidyesMust be a campaign of yours
urlstringyesThe destination. Must be a valid http: or https: URL
descriptionstringno≤500 characters. Printed as a caption beside the code
trackingEnabledbooleannoDefaults to false. Controls only whether each scan is recorded. The printed code encodes the redirect either way, so it does not affect what is printed or whether url can be changed later
bash
curl -X POST https://api.utskick.dr.se/api/v1/qr-codes \
  -H "x-api-key: drse_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "campaignId": "550e8400-e29b-41d4-a716-446655440000",
    "url": "https://example.com/promo",
    "description": "Landing page",
    "trackingEnabled": true
  }'
ts
const res = await fetch("https://api.utskick.dr.se/api/v1/qr-codes", {
  method: "POST",
  headers: {
    "x-api-key": process.env.DRSE_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaignId: "550e8400-e29b-41d4-a716-446655440000",
    url: "https://example.com/promo",
    description: "Landing page",
    trackingEnabled: true,
  }),
});
const { data } = await res.json();
console.log(data.trackingUrl);

201 Created

json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440003",
    "userId": "usr_9f2k7d31",
    "name": "Summer promo",
    "slug": "summer-promo-a1b2",
    "url": "https://example.com/promo",
    "description": "Landing page",
    "trackingEnabled": true,
    "campaignId": "550e8400-e29b-41d4-a716-446655440000",
    "createdAt": "2026-07-25T10:01:00.000Z",
    "updatedAt": "2026-07-25T10:01:00.000Z",
    "trackingUrl": "https://api.utskick.dr.se/api/r/summer-promo-a1b2"
  }
}
FieldNotes
slugDerived from the QR code's name plus a 4-character random suffix. Globally unique and permanent — it is printed on paper
nameThe code's display name. Defaults to the campaign's name when created through this endpoint
campaignIdThe campaign the code is currently linked to, or null when it has been unlinked in the dashboard's Konverteringar view
trackingUrlThe address encoded into the printed code — always, whatever trackingEnabled is set to. Only returned here and on campaign create

Failures ​

StatuscodeWhen
400validation_failedcampaignId not a UUID; url not a valid URL, or not http:/https:; description over 500 characters
401unauthorizedKey problem
403forbiddenKey lacks qr-codes:create
404not_foundCampaign not found — unknown id, or a campaign in another account
409conflictCampaign already has a QR code

Retrieve a QR code ​

GET/api/v1/qr-codes/:id
requires qr-codes:read

Requires qr-codes:read.

bash
curl -H "x-api-key: drse_your_api_key" \
  https://api.utskick.dr.se/api/v1/qr-codes/550e8400-e29b-41d4-a716-446655440003
ts
const id = "550e8400-e29b-41d4-a716-446655440003";
const res = await fetch(`https://api.utskick.dr.se/api/v1/qr-codes/${id}`, {
  headers: { "x-api-key": process.env.DRSE_API_KEY! },
});
const { data } = await res.json();
// Rebuild the tracking URL yourself — it is not returned here:
const trackingUrl = `https://api.utskick.dr.se/api/r/${data.slug}`;

200 OK

json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440003",
    "userId": "usr_9f2k7d31",
    "name": "Summer promo",
    "slug": "summer-promo-a1b2",
    "url": "https://example.com/promo",
    "description": "Landing page",
    "trackingEnabled": true,
    "campaignId": "550e8400-e29b-41d4-a716-446655440000",
    "createdAt": "2026-07-25T10:01:00.000Z",
    "updatedAt": "2026-07-25T10:01:00.000Z"
  }
}

No trackingUrl here

This response deliberately has no trackingUrl field — only the create response and the campaign-create response include it. Compose it from slug: {baseUrl}/api/r/{slug}.

Failures ​

StatuscodeWhen
400validation_failedid is not a UUID (QR code ID must be a valid UUID)
401unauthorizedKey problem
403forbiddenKey lacks qr-codes:read
404not_foundQR code not found — unknown id, or one belonging to another account

Update a QR code ​

PATCH/api/v1/qr-codes/:id
requires qr-codes:update

Requires qr-codes:update. The slug can never change, so a code already in print keeps resolving; changing url re-points every printed code at the new destination.

Body ​

Send only the fields you are changing. Unknown fields are rejected with a 400 rather than ignored.

FieldTypeNotes
urlstringValid http:/https: URL
descriptionstring≤500 characters
trackingEnabledboolean—
bash
curl -X PATCH https://api.utskick.dr.se/api/v1/qr-codes/550e8400-e29b-41d4-a716-446655440003 \
  -H "x-api-key: drse_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/autumn" }'
ts
const id = "550e8400-e29b-41d4-a716-446655440003";
const res = await fetch(`https://api.utskick.dr.se/api/v1/qr-codes/${id}`, {
  method: "PATCH",
  headers: {
    "x-api-key": process.env.DRSE_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ url: "https://example.com/autumn" }),
});
const { data } = await res.json();

200 OK — the updated row, again without trackingUrl.

json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440003",
    "userId": "usr_9f2k7d31",
    "name": "Summer promo",
    "slug": "summer-promo-a1b2",
    "url": "https://example.com/autumn",
    "description": "Landing page",
    "trackingEnabled": true,
    "campaignId": "550e8400-e29b-41d4-a716-446655440000",
    "createdAt": "2026-07-25T10:01:00.000Z",
    "updatedAt": "2026-07-25T11:20:00.000Z"
  }
}

Failures ​

StatuscodeWhen
400validation_failedid not a UUID; an invalid url; description too long; or any field not listed above
401unauthorizedKey problem
403forbiddenKey lacks qr-codes:update
404not_foundQR code not found

Delete a QR code ​

DELETE/api/v1/qr-codes/:id
requires qr-codes:delete

Requires qr-codes:delete.

Printed codes stop working

Deleting a code makes its printed slug resolve to a 404 forever. Mail already in the post will carry a dead QR code. To change where a code points, update url instead.

bash
curl -X DELETE https://api.utskick.dr.se/api/v1/qr-codes/550e8400-e29b-41d4-a716-446655440003 \
  -H "x-api-key: drse_your_api_key"
ts
const id = "550e8400-e29b-41d4-a716-446655440003";
const res = await fetch(`https://api.utskick.dr.se/api/v1/qr-codes/${id}`, {
  method: "DELETE",
  headers: { "x-api-key": process.env.DRSE_API_KEY! },
});
const { data } = await res.json(); // { deleted: true }

200 OK

json
{ "data": { "deleted": true } }

Deleting also removes the code's recorded scans.

Failures ​

StatuscodeWhen
400validation_failedid is not a UUID
401unauthorizedKey problem
403forbiddenKey lacks qr-codes:delete
404not_foundQR code not found