API til kassesystemer

Scan kundens Wallio-kort i kassen, og giv stempler, klip, rabat og træk på gavekort — uden at personalet skal bruge en anden skærm.

Kom i gang

Caféen laver en nøgle i sit dashboard under Kassesystem og API og giver den til jer. Nøglen hører til én café og kan kun se og ændre den cafés kort. Send den i hver anmodning:

Authorization: Bearer wallio_…

Prøv, at nøglen virker:

curl https://wallio.dk/api/v1/me -H "Authorization: Bearer wallio_…"

Alle adresser starter med https://wallio.dk/api/v1, og alle svar er JSON. Nøglen må kun ligge på jeres server eller i kassen — aldrig i en app eller hjemmeside, kunderne kan åbne. Caféen kan fjerne en nøgle når som helst; så svarer API'et 401.

Sådan foregår det ved kassen

  1. Kassens scanner læser QR-koden på kundens kort. Den indeholder en adresse som https://wallio.dk/s/3f9a1c0b….
  2. Send teksten, som den er, til GET /cards/lookup?code=…. I får kortet og allowed_actions: de knapper, der giver mening lige nu.
  3. Når kunden betaler, sendes handlingen til POST /cards/<serial>/actions med en Idempotency-Key.
  4. Kundens kort i Apple Wallet og Google Wallet opdaterer sig selv få sekunder efter. I skal ikke gøre mere.

Adresser

AdresseHvad
GET /meCaféen og nøglen. Til at prøve forbindelsen.
GET /card-typesCaféens korttyper, med signup_url (adressen bag QR-koden på disken — kan fx printes på bonen).
GET /cards/lookup?code=…Slå et kort op ud fra det, scanneren læste.
GET /cards/<serial>Slå et kort op ud fra serienummeret.
GET /cards/<serial>/eventsKortets historik, nyeste først (?limit= 1-200, standard 50).
POST /cards/<serial>/actionsGør noget ved kassen (se herunder).

Kortet

{
  "serial": "3f9a1c0b7d2e4f6a8b9c0d1e",
  "type": "stamp_card",
  "status": "active",
  "card_type_id": "5b1e…",
  "business_name": "Kaffebaren",
  "holder": null,
  "last_activity_at": "2026-09-30T08:12:44.000Z",
  "allowed_actions": ["stamp", "undo"],
  "stamp_card": { "stamps": 7, "goal": 10, "reward": "Gratis kaffe", "reward_ready": false }
}

type er en af stamp_card, punch_card, discount_card, coupon, gift_card og raffle, og kortet har et felt med samme navn med det, der hører til typen. status er active, blocked, expired, used (brugt kupon) eller won (vundet lod).

Beløb er i øre (heltal): 4550 er 45,50 kr. Valutaen er altid DKK.

Handlinger

KortactionHvad
stamp_cardstampGiv stempler. count 1-100 (standard 1). Aldrig ud over fuldt: står kortet på 9 af 10, gives 1.
redeemBelønningen er udleveret. Kortet starter forfra på 0.
undoFortryd det seneste stempel eller den seneste udlevering (inden for 10 minutter).
punch_cardpunchBrug et klip.
sell_punch_cardKunden har købt et nyt klippekort. Klippene lægges oveni.
undoFortryd det seneste klip eller salg (inden for 10 minutter).
discount_carduse_discountRabatten er givet. Kortet ændrer sig ikke; det tæller som et besøg i caféens statistik.
couponuse_couponBrug kuponen én gang. Når den er brugt op, bliver den ugyldig i kundens Wallet.
undoFortryd den seneste brug (inden for 10 minutter).
gift_cardchargeTræk amount øre fra. Afvises, hvis der ikke er nok på kortet.
top_upSæt amount øre på. "gift": true, hvis det er en gave fra caféen og ikke betalt af kunden.
undoFortryd den seneste ændring (inden for 10 minutter).

Højst 10.000 kr pr. gang. Eksempel:

curl -X POST https://wallio.dk/api/v1/cards/3f9a1c0b7d2e4f6a8b9c0d1e/actions \
  -H "Authorization: Bearer wallio_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ordre-10442-stempel" \
  -d '{"action": "stamp", "count": 2}'

Svaret har en besked, der kan vises til personalet, og kortet, som det står nu:

{
  "action": "stamp",
  "message": "2 stempler givet. Kortet står nu på 9 af 10.",
  "card": { …kortet efter handlingen… }
}

Send aldrig det samme to gange

Mister kassen forbindelsen, før svaret kommer, ved den ikke, om stemplet blev givet. Send derfor en Idempotency-Key med hver handling — fx ordrenummeret plus handlingen. Kommer den samme nøgle igen inden for et døgn, får I det første svar igen (med headeren Idempotent-Replayed: true), og der sker ikke noget nyt. Samme nøgle med et andet indhold afvises med 422.

Fejl

{
  "error": { "code": "card_full", "message": "Kortet er allerede fuldt. Indløs belønningen først." },
  "card": { … }
}

message er skrevet til personalet og kan vises direkte. Afvises en handling, kommer kortet med, så kassen kan vise, hvordan det står.

Statuscode
400invalid_json, invalid_action, invalid_count, invalid_amount, invalid_gift, invalid_code, invalid_idempotency_key
401invalid_api_key — nøglen mangler, er forkert eller fjernet
403plan_required — caféens pakke har ikke API'et
404card_not_found — kortet findes ikke eller hører til en anden café
409card_full, card_not_full, no_punches_left, coupon_used, insufficient_balance, nothing_to_undo, card_blocked, wrong_card_type, request_in_progress
413body_too_large
422idempotency_key_reused
429rate_limited — højst 120 anmodninger i minuttet pr. nøgle. Se Retry-After.
500/503server_error, try_again — prøv igen med samme Idempotency-Key

Kundedata

Wallio gemmer ingen navne, mails eller telefonnumre på caféens kunder. Kortet er et tilfældigt nummer. Et personale- eller erhvervskort kan have et navn i holder, som caféen selv har skrevet.

Spørgsmål

Skriv til [email protected]. Vi hjælper gerne med en testcafé og en testnøgle.