Forloy API

v1

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.

Loading diagram...

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_key for 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 value
  • location_id The location where the transaction occurred
  • staff_user_id The staff member who processed the transaction
  • purchase_amount The transaction amount
  • idempotency_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 transaction
  • pos_reference_id Your POS receipt or reference ID
  • pos_terminal_id The POS terminal identifier
  • client_timestamp When the transaction originally occurred (ISO 8601)

Example Request

POST /api/v1/transactions/sync
{
  "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

200 OK
{
  "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

207 Multi-Status
{
  "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_key before storing the transaction locally — never after.
  • Record client_timestamp at 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.