Recording Transactions
This is the core integration workflow — looking up a customer's card and recording their purchase. Every transaction flows through two steps: identify the card, then record the purchase.
Transaction Flow
The diagram below illustrates the end-to-end flow from the moment a customer presents their card to when the transaction result is displayed.
Step 1: Look Up Card
Use POST /api/v1/cards/lookup to retrieve information about a customer's card. There are two ways to identify a card:
qr_code_data— The base64-encoded payload from scanning the customer's QR codebarcode_value— The numeric display ID shown as a barcode on the card
Lookup by QR Code
POST /api/v1/cards/lookup
Content-Type: application/json
Authorization: Bearer <access_token>
{
"qr_code_data": "eyJjYXJkX2luc3RhbmNlX2lkIjoiYTFiMmMzZDQtZTVmNi..."
}Lookup by Barcode
POST /api/v1/cards/lookup
Content-Type: application/json
Authorization: Bearer <access_token>
{
"barcode_value": "90001234567890"
}Response
The response includes the card_instance_id, card type, title, merchant and customer names, status, and the current balance. Below is an example response for a punch card:
{
"success": true,
"data": {
"card_instance_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"card_type": "punch",
"card_title": "Coffee Rewards",
"merchant_name": "Bean & Brew",
"customer_name": "Jane Doe",
"status": "active",
"balance": {
"current_punches": 7,
"punch_requirement": 10,
"pending_rewards": 0,
"lifetime_punches": 27
}
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-01-15T10:30:00.000Z"
}
}The balance object varies depending on the card type. Punch cards include current_punches, punch_requirement, pending_rewards. Cashback cards include the current monetary balance. Discount cards include the current discount rate and tier information.
Important: The lookup endpoint is read-only — it does NOT create a transaction. Use it to display card information to the cashier before recording a purchase.
Step 2: Record Purchase
Use POST /api/v1/transactions/record to record a purchase transaction. You can identify the card by qr_code_data or by card_instance_id (returned from the lookup step).
Required Fields
location_id— The location where the transaction is taking placestaff_user_id— The staff member processing the transactionpurchase_amount— The total purchase amount
Optional Fields
punch_count— Number of punches to add (default: 1, punch cards only)transaction_notes— Free-text notes about the transactionidempotency_key— Unique key to prevent duplicate transactionspos_reference_id— Your POS system's internal transaction referencepos_terminal_id— The terminal or register identifierclient_timestamp— ISO 8601 timestamp from the client device (useful for offline sync)
Punch Cards
For punch cards, include punch_count to specify how many punches to add (defaults to 1). The response tells you the new punch count and whether a reward was earned.
POST /api/v1/transactions/record
Content-Type: application/json
Authorization: Bearer <access_token>
{
"qr_code_data": "eyJjYXJkX2luc3RhbmNlX2lkIjoiYTFiMmMzZDQtZTVmNi...",
"location_id": "loc_001",
"staff_user_id": "staff_042",
"purchase_amount": 4.50,
"punch_count": 1,
"idempotency_key": "txn_20260115_103000_pos3"
}{
"success": true,
"data": {
"transaction_id": "txn_9f8e7d6c-5b4a-3210-fedc-ba0987654321",
"card_type": "punch",
"punches_added": 1,
"new_punch_count": 8,
"reward_earned": false,
"reward": null
},
"meta": {
"request_id": "req_def456",
"timestamp": "2026-01-15T10:30:05.000Z"
}
}When the customer reaches the punch threshold, the response includes reward details:
{
"success": true,
"data": {
"transaction_id": "txn_1a2b3c4d-5e6f-7890-abcd-ef1234567890",
"card_type": "punch",
"punches_added": 1,
"new_punch_count": 10,
"reward_earned": true,
"reward": {
"reward_id": "rwd_abc123",
"title": "Free Coffee",
"description": "One free drink of your choice",
"status": "pending_claim"
}
},
"meta": {
"request_id": "req_ghi789",
"timestamp": "2026-01-15T10:35:00.000Z"
}
}Note: There is a 1-minute cooldown between punch transactions for the same card. Attempting to record another punch within the cooldown period will return a COOLDOWN_ACTIVE error (HTTP 429).
Cashback Cards
For cashback cards, the purchase_amount is used to calculate the cashback earned based on the configured rate. The response includes the cashback earned and the new balance.
POST /api/v1/transactions/record
Content-Type: application/json
Authorization: Bearer <access_token>
{
"card_instance_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"location_id": "loc_001",
"staff_user_id": "staff_042",
"purchase_amount": 85.00,
"idempotency_key": "txn_20260115_104500_pos3"
}{
"success": true,
"data": {
"transaction_id": "txn_cb001122-3344-5566-7788-99aabbccddee",
"card_type": "cashback",
"purchase_amount": 85.00,
"cashback_rate": 0.05,
"cashback_earned": 4.25,
"new_balance": 32.75,
"tier_changed": false
},
"meta": {
"request_id": "req_jkl012",
"timestamp": "2026-01-15T10:45:00.000Z"
}
}Discount Cards
For discount cards, the response includes the discount rate applied, the discount amount, and the final amount the customer should pay.
POST /api/v1/transactions/record
Content-Type: application/json
Authorization: Bearer <access_token>
{
"card_instance_id": "d4e5f6a7-b8c9-0123-4567-890abcdef012",
"location_id": "loc_001",
"staff_user_id": "staff_042",
"purchase_amount": 120.00,
"idempotency_key": "txn_20260115_110000_pos3"
}{
"success": true,
"data": {
"transaction_id": "txn_dc112233-4455-6677-8899-aabbccddeeff",
"card_type": "discount",
"purchase_amount": 120.00,
"discount_rate": 0.10,
"discount_amount": 12.00,
"final_amount": 108.00
},
"meta": {
"request_id": "req_mno345",
"timestamp": "2026-01-15T11:00:00.000Z"
}
}Idempotency
Use the idempotency_key field to prevent duplicate transactions caused by network retries or connectivity issues. If the same key is sent twice, the API returns the original transaction result instead of creating a duplicate.
{
"success": true,
"data": {
"transaction_id": "txn_9f8e7d6c-5b4a-3210-fedc-ba0987654321",
"card_type": "punch",
"punches_added": 1,
"new_punch_count": 8,
"reward_earned": false,
"reward": null,
"idempotent": true
},
"meta": {
"request_id": "req_pqr678",
"timestamp": "2026-01-15T10:30:05.000Z"
}
}When a duplicate request is detected, the response includes "idempotent": true along with the original transaction data.
Recommendation: Generate a UUID or timestamp-based key for each transaction attempt. A good pattern is txn_{date}_{time}_{terminal_id} to ensure uniqueness while remaining debuggable.
Transaction History
Use GET /api/v1/transactions/history to retrieve past transactions for a specific card. This is useful for displaying a customer's transaction history or for reconciliation purposes.
Query Parameters
card_instance_id— Required. The card to retrieve history forpage— Page number (default: 1)page_size— Results per page (default: 20)transaction_type— Filter by type (e.g., punch, cashback, discount)from_date— ISO 8601 start date filterto_date— ISO 8601 end date filter
GET /api/v1/transactions/history?card_instance_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890&page=1&page_size=10
Authorization: Bearer <access_token>{
"success": true,
"data": {
"transactions": [
{
"transaction_id": "txn_9f8e7d6c-5b4a-3210-fedc-ba0987654321",
"transaction_type": "punch",
"purchase_amount": 4.50,
"punches_added": 1,
"created_at": "2026-01-15T10:30:05.000Z",
"location_name": "Downtown Branch",
"staff_name": "Alex M."
},
{
"transaction_id": "txn_1a2b3c4d-5e6f-7890-abcd-ef1234567890",
"transaction_type": "punch",
"purchase_amount": 5.00,
"punches_added": 1,
"created_at": "2026-01-14T09:15:00.000Z",
"location_name": "Airport Kiosk",
"staff_name": "Sam K."
}
],
"pagination": {
"page": 1,
"page_size": 10,
"total_count": 27,
"total_pages": 3
}
},
"meta": {
"request_id": "req_stu901",
"timestamp": "2026-01-15T12:00:00.000Z"
}
}Next Steps
Now that you can look up cards and record transactions, explore these related guides:
- Offline Sync — How to batch sync transactions when connectivity is intermittent
- Rewards & Cashback — How to claim earned rewards and redeem cashback balances