Cash registers scan the customer's Fidelia card (QR or barcode) and add stamps, points, cashback, membership check-ins, prepaid payments and pack items. All amounts are integers in minor units (cents / öre) of the shop's currency. Guides: https://fidelia.se/developers/
Cards
Look up a card and act on it.
GET /cards/{code}
Look up a card
code is what the scanner read: the 32-character serial (QR) or the 13-digit card number (barcode). Returns the customer's first name, balances, rewards ready to use and, for memberships, the discount to apply to the basket.
| Name | In | Description |
|---|
code · required | path | Serial (QR, 32 hex) or card number (barcode, 13 digits). |
lang | query | Language of reward and card names (one of the shop's languages; also read from Accept-Language). |
Responses
200 OK401 Missing, malformed or revoked key (invalid_key)402 The shop needs Pro (plan_required) or its subscription ended (subscription_inactive)404 Not found (card_not_found, receipt_not_found, pairing_not_found, milestone_not_found)410 Gone (card_voided, pairing_expired)429 Too many requests (rate_limited); wait Retry-After seconds
POST /cards/{code}/sale
Record a sale
Send it once the receipt is closed. Fidelia applies the card's till rules: a stamp (stamp cards, from the minimum amount), points (points cards), cashback credit on the part not paid with the card balance, a check-in (memberships). Prepaid and pack cards earn nothing on a sale. A sale never fails for a business reason: applied is false with a reason instead (e.g. cooldown, reward_ready, below_min_amount).
| Name | In | Description |
|---|
code · required | path | Serial (QR, 32 hex) or card number (barcode, 13 digits). |
Idempotency-Key · required | header | Unique per action (e.g. a UUID). A retry with the same key and body replays the first answer (header Idempotent-Replayed: true); kept 24 h. |
Fidelia-Device | header | Till name shown in the shop's history (default: the key's name). |
lang | query | Language of reward and card names (one of the shop's languages; also read from Accept-Language). |
Request body (example)
{
"amount": 1250,
"receipt_ref": "T1-000042"
}
Responses
200 OK400 Invalid input (invalid_input + field)401 Missing, malformed or revoked key (invalid_key)402 The shop needs Pro (plan_required) or its subscription ended (subscription_inactive)403 The key may not do this (scope_denied)404 Not found (card_not_found, receipt_not_found, pairing_not_found, milestone_not_found)409 The card's state refuses it (insufficient_balance, not_ready, already_claimed, wrong_card_type, reverse_conflict, idempotency_mismatch, …)410 Gone (card_voided, pairing_expired)429 Too many requests (rate_limited); wait Retry-After seconds
POST /cards/{code}/rewards/{reward_id}/redeem
Use a reward
Stamp cards: reward_id is ready (the card is full) or milestone:<id> (a smaller reward on the way, listed in stamps.milestones; the card keeps collecting). Points cards: one of the program's reward ids (see GET /me). The answer carries the POS product the owner linked to the reward (product.ref, product.value) so the till can add the free item.
| Name | In | Description |
|---|
code · required | path | Serial (QR, 32 hex) or card number (barcode, 13 digits). |
reward_id · required | path | ready, milestone:<id> or a points reward id |
Idempotency-Key · required | header | Unique per action (e.g. a UUID). A retry with the same key and body replays the first answer (header Idempotent-Replayed: true); kept 24 h. |
Fidelia-Device | header | Till name shown in the shop's history (default: the key's name). |
lang | query | Language of reward and card names (one of the shop's languages; also read from Accept-Language). |
Request body (example)
{
"receipt_ref": "T1-000042"
}
Responses
200 OK400 Invalid input (invalid_input + field)401 Missing, malformed or revoked key (invalid_key)402 The shop needs Pro (plan_required) or its subscription ended (subscription_inactive)403 The key may not do this (scope_denied)404 Not found (card_not_found, receipt_not_found, pairing_not_found, milestone_not_found)409 The card's state refuses it (insufficient_balance, not_ready, already_claimed, wrong_card_type, reverse_conflict, idempotency_mismatch, …)410 Gone (card_voided, pairing_expired)429 Too many requests (rate_limited); wait Retry-After seconds
POST /cards/{code}/balance/spend
Pay with the card balance
Cashback and prepaid cards. amount is a number of minor units or "all"; never more than receipt_total when given. Treat the result's spent as a payment line (split tender: the customer pays the rest another way). Send it before the sale on the same receipt_ref, so cashback isn't earned on the part paid with the balance.
| Name | In | Description |
|---|
code · required | path | Serial (QR, 32 hex) or card number (barcode, 13 digits). |
Idempotency-Key · required | header | Unique per action (e.g. a UUID). A retry with the same key and body replays the first answer (header Idempotent-Replayed: true); kept 24 h. |
Fidelia-Device | header | Till name shown in the shop's history (default: the key's name). |
lang | query | Language of reward and card names (one of the shop's languages; also read from Accept-Language). |
Request body (example)
{
"amount": "all",
"receipt_total": 1250,
"receipt_ref": "T1-000042"
}
Responses
200 OK400 Invalid input (invalid_input + field)401 Missing, malformed or revoked key (invalid_key)402 The shop needs Pro (plan_required) or its subscription ended (subscription_inactive)403 The key may not do this (scope_denied)404 Not found (card_not_found, receipt_not_found, pairing_not_found, milestone_not_found)409 The card's state refuses it (insufficient_balance, not_ready, already_claimed, wrong_card_type, reverse_conflict, idempotency_mismatch, …)410 Gone (card_voided, pairing_expired)429 Too many requests (rate_limited); wait Retry-After seconds
POST /cards/{code}/balance/topup
Top up a prepaid card
The customer paid amount at the till; the card is credited amount plus the program's bonus (credited).
| Name | In | Description |
|---|
code · required | path | Serial (QR, 32 hex) or card number (barcode, 13 digits). |
Idempotency-Key · required | header | Unique per action (e.g. a UUID). A retry with the same key and body replays the first answer (header Idempotent-Replayed: true); kept 24 h. |
Fidelia-Device | header | Till name shown in the shop's history (default: the key's name). |
lang | query | Language of reward and card names (one of the shop's languages; also read from Accept-Language). |
Request body (example)
{
"amount": 2000,
"receipt_ref": "T1-000043"
}
Responses
200 OK400 Invalid input (invalid_input + field)401 Missing, malformed or revoked key (invalid_key)402 The shop needs Pro (plan_required) or its subscription ended (subscription_inactive)403 The key may not do this (scope_denied)404 Not found (card_not_found, receipt_not_found, pairing_not_found, milestone_not_found)409 The card's state refuses it (insufficient_balance, not_ready, already_claimed, wrong_card_type, reverse_conflict, idempotency_mismatch, …)410 Gone (card_voided, pairing_expired)429 Too many requests (rate_limited); wait Retry-After seconds
POST /cards/{code}/membership/renew
Renew or activate a membership
Extends the membership by the program's validity, or activates a membership waiting for payment.
| Name | In | Description |
|---|
code · required | path | Serial (QR, 32 hex) or card number (barcode, 13 digits). |
Idempotency-Key · required | header | Unique per action (e.g. a UUID). A retry with the same key and body replays the first answer (header Idempotent-Replayed: true); kept 24 h. |
Fidelia-Device | header | Till name shown in the shop's history (default: the key's name). |
lang | query | Language of reward and card names (one of the shop's languages; also read from Accept-Language). |
Request body (example)
{
"receipt_ref": "T1-000044"
}
Responses
200 OK400 Invalid input (invalid_input + field)401 Missing, malformed or revoked key (invalid_key)402 The shop needs Pro (plan_required) or its subscription ended (subscription_inactive)403 The key may not do this (scope_denied)404 Not found (card_not_found, receipt_not_found, pairing_not_found, milestone_not_found)409 The card's state refuses it (insufficient_balance, not_ready, already_claimed, wrong_card_type, reverse_conflict, idempotency_mismatch, …)410 Gone (card_voided, pairing_expired)429 Too many requests (rate_limited); wait Retry-After seconds
POST /cards/{code}/pack/sell
Sell or activate a pack
A pack waiting for payment is activated; an active one gets count more packs.
| Name | In | Description |
|---|
code · required | path | Serial (QR, 32 hex) or card number (barcode, 13 digits). |
Idempotency-Key · required | header | Unique per action (e.g. a UUID). A retry with the same key and body replays the first answer (header Idempotent-Replayed: true); kept 24 h. |
Fidelia-Device | header | Till name shown in the shop's history (default: the key's name). |
lang | query | Language of reward and card names (one of the shop's languages; also read from Accept-Language). |
Request body (example)
{
"count": 1,
"receipt_ref": "T1-000045"
}
Responses
200 OK400 Invalid input (invalid_input + field)401 Missing, malformed or revoked key (invalid_key)402 The shop needs Pro (plan_required) or its subscription ended (subscription_inactive)403 The key may not do this (scope_denied)404 Not found (card_not_found, receipt_not_found, pairing_not_found, milestone_not_found)409 The card's state refuses it (insufficient_balance, not_ready, already_claimed, wrong_card_type, reverse_conflict, idempotency_mismatch, …)410 Gone (card_voided, pairing_expired)429 Too many requests (rate_limited); wait Retry-After seconds
POST /cards/{code}/pack/use
Use pack items
Takes count items (1–10) from an active pack.
| Name | In | Description |
|---|
code · required | path | Serial (QR, 32 hex) or card number (barcode, 13 digits). |
Idempotency-Key · required | header | Unique per action (e.g. a UUID). A retry with the same key and body replays the first answer (header Idempotent-Replayed: true); kept 24 h. |
Fidelia-Device | header | Till name shown in the shop's history (default: the key's name). |
lang | query | Language of reward and card names (one of the shop's languages; also read from Accept-Language). |
Request body (example)
{
"count": 1,
"receipt_ref": "T1-000046"
}
Responses
200 OK400 Invalid input (invalid_input + field)401 Missing, malformed or revoked key (invalid_key)402 The shop needs Pro (plan_required) or its subscription ended (subscription_inactive)403 The key may not do this (scope_denied)404 Not found (card_not_found, receipt_not_found, pairing_not_found, milestone_not_found)409 The card's state refuses it (insufficient_balance, not_ready, already_claimed, wrong_card_type, reverse_conflict, idempotency_mismatch, …)410 Gone (card_voided, pairing_expired)429 Too many requests (rate_limited); wait Retry-After seconds
Card
{
"type": "object",
"required": [
"serial",
"card_number",
"type",
"program",
"customer",
"currency",
"rewards_available",
"cooldown_remaining_seconds"
],
"properties": {
"serial": {
"type": "string"
},
"card_number": {
"type": "string",
"pattern": "^[0-9]{13}$"
},
"type": {
"$ref": "#/components/schemas/CardType"
},
"program": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"name": {
"type": [
"string",
"null"
]
}
}
},
"customer": {
"type": "object",
"properties": {
"first_name": {
"type": [
"string",
"null"
]
},
"locale": {
"type": [
"string",
"null"
]
}
}
},
"currency": {
"type": "string",
"enum": [
"EUR",
"SEK"
]
},
"tier": {
"type": [
"object",
"null"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": [
"string",
"null"
]
},
"perk": {
"type": [
"string",
"null"
]
}
}
},
"cooldown_remaining_seconds": {
"type": "integer",
"description": "Seconds before the next stamp / check-in counts."
},
"rewards_available": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"label": {
"type": [
"string",
"null"
]
},
"points": {
"type": "integer"
}
}
},
"description": "Ask the customer \"Use reward now?\" when not empty."
},
"stamps": {
"type": "object",
"description": "Stamp cards.",
"properties": {
"count": {
"type": "integer"
},
"required": {
"type": "integer"
},
"cycle": {
"type": "integer"
},
"reward_ready": {
"type": "boolean"
},
"reward": {
"type": [
"string",
"null"
]
},
"milestones": {
"type": "array",
"description": "Smaller rewards on the way (this round of stamps).",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "milestone:cookie"
},
"stamps": {
"type": "integer"
},
"label": {
"type": [
"string",
"null"
]
},
"claimed": {
"type": "boolean"
},
"available": {
"type": "boolean"
}
}
}
}
}
},
"points": {
"type": "object",
"description": "Points cards.",
"properties": {
"balance": {
"type": "integer"
},
"spend_per_point": {
"type": [
"integer",
"null"
]
},
"rewards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"label": {
"type": [
"string",
"null"
]
},
"points": {
"type": "integer"
},
"available": {
"type": "boolean"
}
}
}
}
}
},
"balance": {
"type": "object",
"description": "Cashback and prepaid cards (minor units).",
"properties": {
"amount": {
"type": "integer"
},
"percent": {
"type": [
"integer",
"null"
]
},
"bonus_percent": {
"type": "integer"
}
}
},
"membership": {
"type": "object",
"description": "Membership cards: apply discount_percent to the basket while active.",
"properties": {
"active": {
"type": "boolean"
},
"waiting_activation": {
"type": "boolean"
},
"valid_until": {
"type": [
"string",
"null"
],
"format": "date"
},
"discount_percent": {
"type": "integer"
},
"perk": {
"type": [
"string",
"null"
]
}
}
},
"pack": {
"type": "object",
"description": "Pack cards.",
"properties": {
"items_left": {
"type": "integer"
},
"items_per_pack": {
"type": "integer"
},
"item": {
"type": [
"string",
"null"
]
},
"active": {
"type": "boolean"
},
"price": {
"type": [
"integer",
"null"
]
}
}
}
}
}
ActionResult
{
"type": "object",
"required": [
"action",
"applied",
"card"
],
"properties": {
"action": {
"type": "string",
"enum": [
"sale",
"redeem",
"balance_spend",
"balance_topup",
"membership_renew",
"pack_sell",
"pack_use"
]
},
"applied": {
"type": "boolean"
},
"reason": {
"type": [
"string",
"null"
],
"description": "Sale only, when applied is false: cooldown, reward_ready, below_min_amount, auto_stamp_off, nothing_to_earn, membership_expired, membership_inactive, not_applicable, program_inactive, subscription_inactive."
},
"event": {
"type": [
"string",
"null"
]
},
"receipt_ref": {
"type": [
"string",
"null"
]
},
"earned_points": {
"type": "integer"
},
"earned_balance": {
"type": "integer"
},
"balance_spent_on_receipt": {
"type": "integer"
},
"redeemed_points": {
"type": "integer"
},
"reward_id": {
"type": "string"
},
"product": {
"type": [
"object",
"null"
],
"properties": {
"ref": {
"type": "string"
},
"value": {
"type": "integer"
}
}
},
"spent": {
"type": "integer"
},
"credited": {
"type": "integer"
},
"added_items": {
"type": "integer"
},
"used_items": {
"type": "integer"
},
"activated": {
"type": "boolean"
},
"card": {
"$ref": "#/components/schemas/Card"
}
}
}