API reference
General rules are in Integration conventions.
POST /api/v1/gate/check-ins
Section titled “POST /api/v1/gate/check-ins”Base: https://test-api.finnsbali.com · Authorization: Bearer <gate token>
POST /api/v1/gate/check-ins HTTP/1.1Host: test-api.finnsbali.comAuthorization: Bearer <gate token>Content-Type: application/json
{ "gateId": "beach-club-main-1", "qr": "FINNS1:q3V0…" }| Field | Rule |
|---|---|
gateId |
Your gate’s id, [a-z0-9-]{1,64}, agreed with FINNS |
qr |
The scanned text, verbatim |
Answers
Section titled “Answers”| Status | Body | Meaning |
|---|---|---|
200 |
{"result": "granted"} |
Let them in. Points were credited. |
200 |
{"result": "denied", "reason": "expired"} |
QR too old; ask the member to refresh |
200 |
{"result": "denied", "reason": "used"} |
QR already used |
200 |
{"result": "denied", "reason": "unknown"} |
Not a QR core issued |
200 |
{"result": "denied", "reason": "malformed"} |
Not a valid QR |
400 |
problem detail, malformed |
Bad gateId or missing field: a bug in the gate |
401 |
empty | Wrong or missing gate token |
A denial is a normal 200, not an error. Treat unknown future reason values as a generic denial.
Client rules
Section titled “Client rules”The timings below are examples. What matters is the behaviour: one scan at a time, and no entry without granted.
- Set a timeout (for example 5 seconds) and treat it as “unavailable”; never let a guest in without
granted. - Handle one scan at a time; ignore the same text for a short while after its result (for example 10 seconds).
- Refuse locally, without calling core, anything that doesn’t start with
FINNS; show “this gate can’t read this code yet” for an unknownFINNSn:version (the gate needs an update). - Never log the QR text or the token.
Internal and confidential. Share partner sections only as partner builds (pnpm build:partner).

