A scan is not a check-in
A scanner reading a QR code proves only that it can see a ticket identifier. Your backend must authenticate staff and confirm that the ticket belongs at this entry point. Eventlayer then validates the ticket lifecycle and active ticket type as part of recording admission. An API key grants workspace access; it does not define your venue’s door policy.
Make one atomic transition
The check-in operation should validate the ticket and write its checked-in state within one database transaction. Two devices scanning simultaneously must not both believe they completed the first entry.
UPDATE tickets
SET status = 'checked_in', checked_in_at = now()
WHERE public_id = $1 AND status = 'assigned'
RETURNING *;A separate read followed by an unconditional write leaves a gap for another scanner. The condition belongs in the write itself. A previous lookup showing “assigned” is useful context, not permission to admit.
Treat retries as expected
Mobile networks drop responses. A client may repeat a request even though the first attempt committed. Eventlayer returns TICKET_ALREADY_CHECKED_IN when the ticket has already been used, rather than recording a second admission.
A lost response is still an unknown outcome for your scanner. Fetch the ticket and ask staff to reconcile it. Seeing “checked in” cannot prove that this device succeeded: another device may have admitted the guest. Never turn a duplicate or unknown result into a new green success screen.
Keep the door interface simple
The scanning client should render a small set of explicit outcomes: admitted, already admitted, inactive ticket type, cancelled ticket, unassigned ticket, or not found. Operational nuance belongs in the API response, not in guesswork on the device.