Appearance
Webhooks
StoreFront sends real-time webhook notifications to your system when transactions occur. This enables you to:
- Track purchases in real-time
- Fulfil cashback or points rewards immediately
- Update your internal reporting systems
Standard Webhooks
StoreFront webhooks are compliant with the Standard Webhooks specification. This means you can use any Standard Webhooks compatible library to verify and process webhooks.
For details on signature verification, security best practices, and operational considerations, refer to the Standard Webhooks specification.
Headers
Every webhook includes the following Standard Webhooks headers:
| Header | Description |
|---|---|
webhook-id | Unique identifier for this webhook (use as idempotency key) |
webhook-timestamp | Unix timestamp of when the webhook was sent |
webhook-signature | HMAC-SHA256 signature for verification |
Endpoint Requirements
Your webhook endpoint must:
- Accept
POSTrequests - Use HTTPS with a valid SSL certificate
- Return a
2xxstatus code to acknowledge receipt - Respond within 30 seconds
Async Processing
Process webhooks asynchronously and return 202 Accepted immediately. This prevents timeout issues if your processing takes time.
Retries
If delivery fails, StoreFront retries with exponential backoff:
| Attempt | Delay | Cumulative Time |
|---|---|---|
| 1 | Immediate | 0 |
| 2 | 5 seconds | 5 seconds |
| 3 | 5 minutes | ~5 minutes |
| 4 | 30 minutes | ~35 minutes |
| 5 | 2 hours | ~2.5 hours |
| 6 | 5 hours | ~7.5 hours |
| 7 | 10 hours | ~17.5 hours |
| 8 | 14 hours | ~31.5 hours |
| 9 | 20 hours | ~51.5 hours |
Event Types
StoreFront sends webhooks for two transaction events:
| Event | Description |
|---|---|
| Sale | A purchase has been completed and fulfilled |
| Cancel | A previously completed transaction has been cancelled |
The specific event type depends on your pricing model:
| Pricing Model | Sale Event | Cancel Event |
|---|---|---|
| Face Value | sale.face_value | cancel.face_value |
| Discount | sale.discount | cancel.discount |
| Cashback | sale.cashback | cancel.cashback |
StoreFront-Specific Behaviour
Per-Item Webhooks
StoreFront supports multi-item baskets, but webhooks are sent per item, not per order.
Gift cards are fulfilled individually through Tillo's API, where each brand may be processed by a different upstream supplier. In the event of an upstream issue - such as temporary supplier unavailability or stock constraints - individual items within an order may be affected independently.
As a result:
- Each successfully fulfilled item triggers its own webhook
- Each webhook contains an
order_idto group items from the same order - Each item has a unique
transaction_id - If an item experiences a fulfilment delay, its webhook will be sent once the issue is resolved
Immutable Transactions
Transactions are immutable - there is no "status change" concept:
- A sale webhook indicates a successful purchase
- A cancel webhook is a separate event for reversals
- Match cancellations to sales using
transaction_id
Payload Reference
The payload structure varies by pricing model. All payloads follow the Standard Webhooks format with type, timestamp, and data fields.
Discount Model
Sale Webhook
json
{
"type": "sale.discount",
"timestamp": "2025-02-26T13:08:09Z",
"data": {
"product": {
"code": "xbox-usa",
"name": "Xbox"
},
"order_id": "2515839709",
"member_id": "8788698",
"transaction_id": "2515839709-365402",
"created_date": "2025-02-26T13:08:01Z",
"purchase_amount": "100.00",
"buyer_discount": {
"amount": "8.70",
"percentage": "8.70"
},
"member_discount": {
"amount": "6.50",
"percentage": "6.50"
},
"incentive": {
"percentage": "1.50",
"amount": "1.50"
}
}
}{
"type": "sale.discount",
"timestamp": "2025-02-26T13:08:09Z",
"data": {
"product": {
"code": "xbox-usa",
"name": "Xbox"
},
"order_id": "2515839709",
"member_id": "8788698",
"transaction_id": "2515839709-365402",
"created_date": "2025-02-26T13:08:01Z",
"purchase_amount": "100.00",
"buyer_discount": {
"amount": "8.70",
"percentage": "8.70"
},
"member_discount": {
"amount": "6.50",
"percentage": "6.50"
},
"incentive": {
"percentage": "1.50",
"amount": "1.50"
}
}
}Cancel Webhook
json
{
"type": "cancel.discount",
"timestamp": "2025-01-01T15:48:01Z",
"data": {
"order_id": "2515839709",
"member_id": "8788698",
"transaction_id": "2515839709-365402",
"created_date": "2025-01-01T15:48:01Z"
}
}{
"type": "cancel.discount",
"timestamp": "2025-01-01T15:48:01Z",
"data": {
"order_id": "2515839709",
"member_id": "8788698",
"transaction_id": "2515839709-365402",
"created_date": "2025-01-01T15:48:01Z"
}
}Cashback Model
Sale Webhook
json
{
"type": "sale.cashback",
"timestamp": "2025-02-26T13:08:09Z",
"data": {
"product": {
"code": "xbox-usa",
"name": "Xbox"
},
"order_id": "2515839709",
"member_id": "8788698",
"transaction_id": "2515839709-365402",
"created_date": "2025-02-26T13:08:01Z",
"purchase_amount": "100.00",
"buyer_discount": {
"amount": "8.70",
"percentage": "8.70"
},
"member_cashback": {
"amount": "6.50",
"percentage": "6.50"
},
"incentive": {
"percentage": "1.50",
"amount": "1.50"
}
}
}{
"type": "sale.cashback",
"timestamp": "2025-02-26T13:08:09Z",
"data": {
"product": {
"code": "xbox-usa",
"name": "Xbox"
},
"order_id": "2515839709",
"member_id": "8788698",
"transaction_id": "2515839709-365402",
"created_date": "2025-02-26T13:08:01Z",
"purchase_amount": "100.00",
"buyer_discount": {
"amount": "8.70",
"percentage": "8.70"
},
"member_cashback": {
"amount": "6.50",
"percentage": "6.50"
},
"incentive": {
"percentage": "1.50",
"amount": "1.50"
}
}
}Cancel Webhook
json
{
"type": "cancel.cashback",
"timestamp": "2025-01-01T15:48:01Z",
"data": {
"order_id": "f9s8e7f9f7",
"member_id": "7fhw8ekd-73js-0sl1-m18s-8ejamaleruto",
"transaction_id": "f9s8e7f9f7",
"created_date": "2025-01-01T15:48:01Z"
}
}{
"type": "cancel.cashback",
"timestamp": "2025-01-01T15:48:01Z",
"data": {
"order_id": "f9s8e7f9f7",
"member_id": "7fhw8ekd-73js-0sl1-m18s-8ejamaleruto",
"transaction_id": "f9s8e7f9f7",
"created_date": "2025-01-01T15:48:01Z"
}
}Face Value Model
Sale Webhook
json
{
"type": "sale.face_value",
"timestamp": "2025-02-26T13:08:09Z",
"data": {
"product": {
"code": "xbox-usa",
"name": "Xbox"
},
"order_id": "1536485736",
"member_id": "0293751",
"transaction_id": "1536485736-365400",
"created_date": "2025-02-26T13:08:01Z",
"purchase_amount": "100.00",
"buyer_discount": {
"amount": "8.70",
"percentage": "8.70"
}
}
}{
"type": "sale.face_value",
"timestamp": "2025-02-26T13:08:09Z",
"data": {
"product": {
"code": "xbox-usa",
"name": "Xbox"
},
"order_id": "1536485736",
"member_id": "0293751",
"transaction_id": "1536485736-365400",
"created_date": "2025-02-26T13:08:01Z",
"purchase_amount": "100.00",
"buyer_discount": {
"amount": "8.70",
"percentage": "8.70"
}
}
}Cancel Webhook
json
{
"type": "cancel.face_value",
"timestamp": "2025-03-13T09:32:55Z",
"data": {
"order_id": "1536485736",
"member_id": "0293751",
"transaction_id": "1536485736-365400",
"created_date": "2025-02-26T13:08:01Z"
}
}{
"type": "cancel.face_value",
"timestamp": "2025-03-13T09:32:55Z",
"data": {
"order_id": "1536485736",
"member_id": "0293751",
"transaction_id": "1536485736-365400",
"created_date": "2025-02-26T13:08:01Z"
}
}Field Reference
Sale Payload Fields
| Field | Type | Description |
|---|---|---|
type | string | Event type (e.g., sale.discount, sale.cashback, sale.face_value) |
timestamp | string | When the webhook was sent (ISO 8601 UTC) |
data.product.code | string | Tillo product code |
data.product.name | string | Product display name |
data.order_id | string | Order identifier (groups items in multi-item orders) |
data.member_id | string | Your user identifier from authentication (if provided) |
data.transaction_id | string | Unique transaction identifier for this item |
data.created_date | string | When the transaction occurred (ISO 8601 UTC) |
data.purchase_amount | string | Face value of the gift card (decimal string) |
data.buyer_discount.amount | string | Your discount from Tillo (decimal string) |
data.buyer_discount.percentage | string | Your discount percentage |
data.member_discount.* | string | Discount applied to user (Discount model only). Inclusive of incentive values when configured and the incentivised payment gateway is selected |
data.member_cashback.* | string | Cashback to award user (Cashback model only). Inclusive of incentive values when configured and the incentivised payment gateway is selected |
data.incentive.amount | string | Incremental incentive amount included in member_*.amount (e.g. member_discount.amount). Base amount can be derived as member_*.amount - incentive.amount |
data.incentive.percentage | string | Incremental incentive percentage included in member_*.percentage (e.g. member_discount.amount). Base percentage can be derived as member_*.percentage - incentive.percentage |
Cancel Payload Fields
| Field | Type | Description |
|---|---|---|
type | string | Event type (e.g., cancel.discount, cancel.cashback, cancel.face_value) |
timestamp | string | When the webhook was sent (ISO 8601 UTC) |
data.order_id | string | Order identifier |
data.member_id | string | Your user identifier from authentication (if provided) |
data.transaction_id | string | Transaction identifier (use to match to original sale) |
data.created_date | string | When the original transaction occurred (ISO 8601 UTC) |
Handling Cancellations
When you receive a cancel webhook:
- Look up the original sale using
transaction_id - Reverse any actions taken:
- Cashback: Deduct the awarded amount from the user's balance
- Points: Reverse the points credited
- Reporting: Update your records
- Return a
2xxresponse