Skip to content

Changelog ​

Notable changes to the developer-facing API. The versioned surface is /api/v1; breaking changes would ship as a new version rather than altering v1.

2026-08-29 — phoneForwardTo removed from campaign create ​

Removed — phoneForwardTo on POST /api/v1/campaigns. The field was session-only (API-key requests always got 422) and is now gone entirely: phone numbers are managed as standalone resources under the dashboard's Konverteringar view, where linking a number to an utskick is a paid step. Requests that still send the field have it silently ignored, like any other unknown field.

Added — userId and name on every QR code object. QR codes are now standalone resources owned by your account: they can be created, renamed, linked and unlinked in the dashboard's Konverteringar view independently of any campaign. Every /api/v1/qr-codes response object therefore now includes userId (always your own account) and name (defaults to the campaign's name when the code is created through the API).

Changed — campaignId may be null. A QR code unlinked in the dashboard keeps its slug and keeps redirecting, but no longer belongs to a campaign; in responses its campaignId is then null. Codes created through /api/v1 are always linked at creation, so integrations that only read back what they created will keep seeing a UUID — but parsers should accept null.

Scans of a printed letter are always attributed to the campaign the letter was generated for, even if the code is later linked to another campaign.

2026-07-27 — Rate-limit headers, and a per-account ceiling ​

Added — rate-limit headers. Every /api/v1 response now carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and RateLimit-Policy, and every 429 carries Retry-After. The documentation previously stated that no such headers existed and that you should assume a fixed 60-second wait; you can now read your exact position and the exact wait. All five names are CORS-exposed, so browser clients can read them too.

RateLimit-Limit/-Remaining/-Reset describe whichever policy is closest to running out; RateLimit-Policy lists them all.

Added — a per-account rate limit. 300 requests / 60 seconds with a burst of 60, covering all of an account's keys and sessions together. The per-key limit is unchanged at 100 requests / 60 seconds, and remains the tighter of the two, so a single-key integration sees no change in behaviour.

If you run several keys concurrently, note that they no longer multiply your total throughput: issuing a second key used to double it, and now both draw on the one account ceiling. Separate keys still isolate one workload's per-key budget from another's.

Added — a per-IP flood shield. Applied per network address, well above the account ceiling, so ordinary integration traffic will not meet it. Its threshold is not published and does not appear in RateLimit-Policy. Its 429 still carries Retry-After.

No breaking changes. No path, method, request schema, response body, status code or existing error message changed. code remains rate_limited for every 429 — the existing API key rate limit exceeded message is untouched, and two new messages distinguish the new tiers:

Limitmessage
Per key (unchanged)API key rate limit exceeded
Per account (new)Account rate limit exceeded
Per IP (new)Too many requests from this IP address

Branch on code; read message only to tell the tiers apart.

See Rate limits.

2026-07-25 — Initial public documentation ​

First published documentation of the API-key surface: 18 permission-scoped operations plus 2 public endpoints.

Available

  • Campaigns — create (unified content + recipients), list, count by status, retrieve for polling.
  • Lists — create, list with recipient counts, read a list's field schema.
  • Recipients — list account-wide or per list, add one, bulk-import from CSV or Excel.
  • QR codes — attach to a campaign, retrieve, update, delete.
  • Templates — list and retrieve (read-only).
  • Public — health check, and the printed QR redirect.

Behaviour worth noting

  • Campaign creation is all-or-nothing: every recipient row is validated before any write.
  • Idempotency-Key on campaign create replays the original campaign with 200 instead of creating a duplicate.
  • API keys are created only in the web app. Key-management and template-mutation endpoints reject keys with 403.
  • Rate limit: 100 requests per 60 seconds per key.
  • Lists are deleted after 90 days without activity; their campaigns are retained with listId: null.

Known limitations

  • QR scan data cannot be read back. With trackingEnabled: true each scan is recorded, but no endpoint returns them and no web app screen shows them. Use your own landing-page analytics for response measurement.
  • QR codes are managed only over the API. The web app sends a qrCode at campaign-create time and offers no screen to view, retarget or delete a code afterwards — so persist the id from the create response if you may need to change the destination later.

Planned ​

Raw editor content will be rejected for API keys. POST /api/v1/campaigns currently accepts a content field carrying a rich-text document. It is not supported for API integrations and will be refused for API-key callers in a future release.

The reason is proof: a template was rendered and approved on screen, and a PDF you produced can be opened and checked — but a document assembled in code and posted straight to the API has been seen by nobody before it is printed.

If you are using content today, migrate to a templateId or a pdf part. See Campaigns.

How changes reach these docs ​

The documentation is checked against the code by tests that run in CI:

  • The registered route table must match the platform's operation inventory.
  • Every documented response shape is asserted against real handler output.
  • Every operation the API exposes must be documented, and no page may document one that does not exist.

So an endpoint cannot be added, removed or renamed without these pages being updated in the same change. Contributors: see docs/api/maintenance.md in the repository.