KortiDocs

Changelog

What changed in the API and the dashboard, newest first.

25 October 2026: a reward only on a full card, and one renewal per pass every 10 minutes

Three rules that stop a new stamp round or a later validUntil from slipping past billing.

  • Redeeming a stamp card that isn’t full is a 409 reward_not_ready everywhere. POST /v1/passes/{id}/redeem already did this; the dashboard and the scanner now do too.
  • A full stamp card can’t be lowered with PATCH /v1/passes/{id} to a number below the total (409 card_full). Setting values.stamps to 0 still starts a new round and is billed as one.
  • A pass can get a new round or a later validUntil once every 10 minutes. Asking again sooner is a 409 renewal_too_soon. Undoing a renewal frees the wait; trial and test passes aren’t limited.

19 October 2026: warnings, onDevice and readable validUntil dates on passes

Every pass has two new fields, warnings and onDevice, so existing integrations keep working.

  • warnings lists what the request asked for that the pass didn’t get. valid_until_capped means a trial pass (every pass a test key issues) can’t be valid past its expiry, so your later values.validUntil was replaced by the expiry date. Before, this happened with no sign in the response.
  • onDevice.apple is true when at least one iPhone has registered the pass with Apple Wallet. saved.apple keeps its meaning: the .pkpass file was downloaded, which can also be a script or a cancelled add. onDevice.google equals saved.google.
  • A values.validUntil sent as an ISO date (2026-12-31) is now written like Korti’s own default, in the card’s language (31 Dec 2026, 31. des. 2026), instead of the raw text.

19 October 2026: a redeemed coupon looks used in the wallet

Redeeming a coupon now shows Used with the date on the customer’s card and greys it out in Apple Wallet and Google Wallet. Apple’s lock screen says it too. The pass keeps status: "active", and undoing the redeem makes the card valid again. Apple Wallet is also no longer told about passes that didn’t change, so it stops re-downloading them.

16 October 2026: test passes are marked in events and kept out of the dashboard

Webhook events and GET /v1/events now have test: true for events of passes a test key issued, false for all others. It is a new field on every event, so existing integrations keep working.

  • In the dashboard, passes issued with a test key are hidden from the customers lists unless you choose Sýna kort frá prufulykli, and are left out of the home figures, the wallet chart, the card page and the activity feed.
  • Updates you send to a card’s customers never go to test passes.

16 October 2026: custom values by their listed name

values now accepts custom.<key>, the name GET /v1/cards lists. Before, only the nested form worked and the listed name was refused as a misspelling.

  • { "custom.member_number": "M-0042" } and { "custom": { "member_number": "M-0042" } } store the same value, on POST /v1/passes and PATCH /v1/passes/{id}. The nested form still works.
  • A key sent in both forms in one request gets 422 invalid_request naming both. A dotted key Korti can’t use gets an error that points to the nested form.
  • Issue a pass now explains custom values and how they differ from the built-in memberNumber.

16 October 2026: limits per business

Request limits now apply to the whole business, not just each key. Normal use is unaffected.

  • All the keys of a business share 300 requests a minute, on top of 100 a minute per key. Over either limit you get 429 rate_limited with Retry-After, as before.
  • A business can have up to 10 active API keys. Revoke one to create another.
  • Korti remembers at most 50,000 idempotent responses; the oldest are forgotten first.

16 October 2026: input limits

The API now bounds what it accepts. Requests inside these limits are unaffected.

  • A request body can be up to 64 KB. Larger ones get 413 payload_too_large.
  • values.custom can have up to 20 keys, and a pass can have up to 20 custom values in total. More gets 422.
  • One points or balance change (delta) can be up to 1,000,000 either way, and a pass holds up to 1,000,000,000 points, stamps, visits or kr. Beyond that you get 422 value_out_of_range.
  • Idempotency keys are now shared by all keys of a business (live keys with live keys, test keys with test keys), and responses over 16 KB aren’t remembered, though they’re still returned.

16 October 2026

Test keys issue trial passes only. Before this, test keys and live keys reached the same passes.

  • A test key (sk_test_…) always issues a trial pass: it expires, is never billed and doesn’t count toward your trial cards, even after your business has gone live.
  • A test key can read, update and void only the passes test keys issued. Other passes answer 404, and the list and event endpoints show only test passes.
  • Test keys can issue 100 passes in any seven days. After that you get 403 test_limit until older test passes expire.
  • Passes have a new test field. Live keys behave as before.

10 October 2026

The Korti API, version 1. The first public version of the API.

  • Cards: GET /v1/cards and GET /v1/cards/{id}, with each card’s variables.
  • Passes: issue, list, get, update, add stamps, add points, change a balance, redeem and void.
  • Events: GET /v1/events, and webhooks signed with Korti-Signature.
  • Idempotency-Key on every POST and PATCH, and a limit of 100 requests a minute per key.
  • An OpenAPI 3.1 spec at /v1/openapi.json, and an interactive explorer.
  • API keys and webhooks in the dashboard, under Developers.