Offline Sync
When your POS system loses internet connectivity, you can queue transactions locally and sync them in batch when connectivity is restored.
Sync Flow
The sequence diagram below illustrates the store-and-forward pattern used for offline transaction syncing.
How It Works
Offline sync follows a store-and-forward pattern. When your POS cannot reach the Forloy API, you scan cards and record transactions into a local queue. Once connectivity is restored, you send those queued transactions in a single batch request.
- When offline, scan cards and record transactions locally.
- Generate a unique
idempotency_keyfor each transaction (UUID v4 recommended). - Store the following fields per transaction:
qr_code_data,location_id,staff_user_id,purchase_amount,client_timestamp,idempotency_key. - When online, batch up to 100 transactions per request.
Sync Request
Send a batch of offline transactions via POST /api/v1/transactions/sync. The request body contains a transactions array with 1 to 100 items.
Required Fields (per item)
qr_code_data— The scanned QR code valuelocation_id— The location where the transaction occurredstaff_user_id— The staff member who processed the transactionpurchase_amount— The transaction amountidempotency_key— A unique identifier to prevent duplicate processing
Optional Fields (per item)
punch_count— Number of punches to award (punch cards only)transaction_notes— Free-text notes about the transactionpos_reference_id— Your POS receipt or reference IDpos_terminal_id— The POS terminal identifierclient_timestamp— When the transaction originally occurred (ISO 8601)
Example Request
{
"transactions": [
{
"qr_code_data": "forloy_enr_abc123def456",
"location_id": "loc_01HQXYZ",
"staff_user_id": "usr_staff_001",
"purchase_amount": 24.50,
"idempotency_key": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"client_timestamp": "2026-03-10T14:22:05.000Z"
},
{
"qr_code_data": "forloy_enr_ghi789jkl012",
"location_id": "loc_01HQXYZ",
"staff_user_id": "usr_staff_001",
"purchase_amount": 12.00,
"punch_count": 1,
"idempotency_key": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"pos_reference_id": "receipt-20260310-042",
"pos_terminal_id": "terminal-03",
"client_timestamp": "2026-03-10T14:25:30.000Z"
}
]
}Response Handling
The sync endpoint returns one of two status codes depending on the outcome.
200— All transactions in the batch succeeded.207 Multi-Status— Some transactions succeeded and some failed. Check each result individually.
The response body always includes total_count, success_count, error_count, and a results array. Each result contains the idempotency_key and a result object with success, idempotent, transaction_id, card_type, or error and message on failure.
200 — All Succeeded
{
"success": true,
"data": {
"total_count": 2,
"success_count": 2,
"error_count": 0,
"results": [
{
"idempotency_key": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"result": {
"success": true,
"idempotent": false,
"transaction_id": "txn_01HQ0001",
"card_type": "cashback"
}
},
{
"idempotency_key": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"result": {
"success": true,
"idempotent": false,
"transaction_id": "txn_01HQ0002",
"card_type": "punch"
}
}
]
},
"meta": {
"request_id": "req_sync_abc123",
"timestamp": "2026-03-10T15:00:12.000Z"
}
}207 — Mixed Results
{
"success": true,
"data": {
"total_count": 2,
"success_count": 1,
"error_count": 1,
"results": [
{
"idempotency_key": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"result": {
"success": true,
"idempotent": true,
"transaction_id": "txn_01HQ0001",
"card_type": "cashback"
}
},
{
"idempotency_key": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"result": {
"success": false,
"error": "CARD_NOT_FOUND",
"message": "No active card found for the given code"
}
}
]
},
"meta": {
"request_id": "req_sync_def456",
"timestamp": "2026-03-10T15:00:12.000Z"
}
}Retry Strategy
After receiving a sync response, iterate through the results by idempotency_key and handle each outcome accordingly.
- Successful items — Remove from the local queue.
- Items with
"idempotent": true— Safe to remove. These were already processed in a previous sync and were not duplicated. - Failed items — Retry in the next sync batch, unless the error is permanent.
Permanent Errors
Stop retrying and flag for manual review when you encounter these errors:
CARD_NOT_FOUND— The card does not exist or was deleted.CARD_INACTIVE— The card exists but has been deactivated.
Transient Errors
These errors are temporary and should be retried in the next sync batch:
INTERNAL_ERROR— An unexpected server error occurred.DATABASE_ERROR— A database operation failed.
Best Practices
- Always generate the
idempotency_keybefore storing the transaction locally — never after. - Record
client_timestampat the time of scan, not at sync time. - Keep batch sizes reasonable (50-100 transactions) to avoid request timeouts.
- Sync frequently when online to minimize stale data and reduce the window for conflicts.
Important: A card may have been deactivated while your POS was offline. Always handle CARD_INACTIVE errors gracefully and flag them for staff review rather than retrying indefinitely.
Next Step
Learn how to claim punch card rewards and redeem cashback balances in the Rewards & Cashback guide.