Documentation Index
Fetch the complete documentation index at: https://docs.withgale.com/llms.txt
Use this file to discover all available pages before exploring further.
Webhooks
Webhooks allow your application to receive real-time notifications when events happen in your Gale account, such as completed payments, failed charges, and refunds.
How Webhooks Work
- Customer completes payment on Gale’s checkout page
- Gale processes the payment
- Gale sends a POST request to your webhook endpoint with event data
- Your server processes the event (e.g., fulfills order, sends email)
- Your server returns a
200 OK response
- Event is marked as delivered successfully
If your server doesn’t respond with 200, Gale retries the delivery automatically.
Setup
1. Create an Endpoint on Your Server
app.post('/webhooks/gale', async (req, res) => {
// Acknowledge immediately — process async
res.status(200).send('OK');
const event = req.body;
switch (event.type) {
case 'order.completed':
await fulfillOrder(event.data);
break;
case 'order.failed':
await notifyOrderFailed(event.data);
break;
case 'refund.succeeded':
await handleRefund(event.data);
break;
}
});
2. Register It in the Dashboard
- Log in to Gale Dashboard
- Go to Settings → Webhooks
- Click Add Endpoint
- Enter your webhook URL
- Select the events to subscribe to
- Copy the secret shown — it is only displayed once
Authentication
Every request Gale sends to your endpoint includes a Bearer token in the Authorization header:
Authorization: Bearer <your-endpoint-secret>
Validate this on your server to confirm the request is genuinely from Gale:
app.post('/webhooks/gale', (req, res) => {
const token = req.headers['authorization']?.replace('Bearer ', '');
if (token !== process.env.GALE_WEBHOOK_SECRET) {
return res.status(401).send('Unauthorized');
}
res.status(200).send('OK');
// process event...
});
Retry Schedule
If your endpoint doesn’t return 200, Gale retries with increasing delays:
| Attempt | Delay after previous failure |
|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 6 hours |
After all retries are exhausted the delivery is marked as failed. You can see failed deliveries in the dashboard under Settings → Webhooks.
Webhook Events
| Event | When it fires |
|---|
order.completed | Payment captured successfully |
order.failed | Payment failed or declined |
refund.created | Refund initiated by merchant |
refund.succeeded | Refund processed successfully |
refund.failed | Refund attempt failed |
Event Envelope
Every event shares the same top-level structure:
{
"id": "evt_01ABC...",
"type": "order.completed",
"created_at": "2026-03-31T12:00:00Z",
"data": { ... }
}
Use id for deduplication — Gale may deliver the same event more than once during retries.
Event Payload Examples
order.completed
Fires when payment is captured and the order is complete. Use this to trigger fulfillment.
{
"id": "evt_01ABC...",
"type": "order.completed",
"created_at": "2026-03-31T12:00:00Z",
"data": {
"id": "ord_01ABC...",
"checkout_id": "cs_abc123",
"status": "completed",
"is_recurring": false,
"payment_status": "captured",
"amount": 4995,
"currency": "usd",
"payment_link_id": "pl_abc123",
"client_reference_id": "your-order-123",
"reference_id": null,
"customer": {
"email": "jane@example.com",
"name": "Jane Doe"
},
"line_items": [
{
"product_id": "PROD-001",
"name": "Digital Thermometer",
"quantity": 1,
"unit_amount": 4995,
"amount": 4995,
"currency": "usd",
"is_recurring": false
}
],
"created_at": "2026-03-31T12:00:00Z"
}
}
payment_link_id and client_reference_id are present for payment link orders. For checkout session orders, reference_id is set instead and payment_link_id is null.
order.failed
Fires when payment fails or is declined.
{
"id": "evt_01DEF...",
"type": "order.failed",
"created_at": "2026-03-31T12:01:00Z",
"data": {
"id": "ord_01DEF...",
"checkout_id": "cs_def456",
"status": "failed",
"is_recurring": false,
"payment_status": "failed",
"amount": 4995,
"currency": "usd",
"payment_link_id": "pl_abc123",
"client_reference_id": "your-order-123",
"reference_id": null,
"failure_reason": "Payment declined",
"customer": {
"email": "jane@example.com",
"name": "Jane Doe"
},
"created_at": "2026-03-31T12:01:00Z"
}
}
refund.created
Fires when a refund is initiated. A refund.succeeded or refund.failed will follow.
{
"id": "evt_01GHI...",
"type": "refund.created",
"created_at": "2026-03-31T13:00:00Z",
"data": {
"id": "ref_01GHI...",
"order_id": "ord_01ABC...",
"checkout_id": "cs_abc123",
"amount": 4995,
"currency": "usd",
"reason": "Customer request",
"payment_link_id": "pl_abc123",
"client_reference_id": "your-order-123",
"reference_id": null,
"customer": {
"email": "jane@example.com",
"name": "Jane Doe"
},
"initiated_at": "2026-03-31T13:00:00Z"
}
}
refund.succeeded
Fires when the refund is processed. Check payment_status to know if the order is fully or partially refunded.
{
"id": "evt_01JKL...",
"type": "refund.succeeded",
"created_at": "2026-03-31T13:01:00Z",
"data": {
"id": "ref_01GHI...",
"order_id": "ord_01ABC...",
"checkout_id": "cs_abc123",
"amount": 4995,
"currency": "usd",
"order_status": "completed",
"payment_status": "refunded",
"reason": "Customer request",
"payment_link_id": "pl_abc123",
"client_reference_id": "your-order-123",
"reference_id": null,
"customer": {
"email": "jane@example.com",
"name": "Jane Doe"
},
"refunded_at": "2026-03-31T13:01:00Z"
}
}
payment_status will be refunded for a full refund or partially_refunded if only part of the order was refunded.
refund.failed
Fires when the refund attempt fails.
{
"id": "evt_01MNO...",
"type": "refund.failed",
"created_at": "2026-03-31T13:02:00Z",
"data": {
"id": "ref_01GHI...",
"order_id": "ord_01ABC...",
"checkout_id": "cs_abc123",
"amount": 4995,
"currency": "usd",
"failure_reason": "Processor declined refund",
"payment_link_id": "pl_abc123",
"client_reference_id": "your-order-123",
"reference_id": null,
"customer": {
"email": "jane@example.com",
"name": "Jane Doe"
},
"failed_at": "2026-03-31T13:02:00Z"
}
}
Status Fields
status — Fulfillment status
| Value | Meaning |
|---|
completed | Payment captured, ready to fulfill |
failed | Payment failed |
cancelled | Order cancelled |
payment_status — Payment state
| Value | Meaning |
|---|
captured | Payment successfully captured |
failed | Payment failed or declined |
refunded | Fully refunded |
partially_refunded | Partially refunded |
Best Practices
- Return 200 immediately, then process the event asynchronously
- Deduplicate using
id — Gale may send the same event more than once during retries
- Don’t rely on webhooks alone — use
GET /v2/checkout/{checkout_id} to poll status if a webhook is missed
- Always validate the Bearer token before processing
Missed Webhooks — Status Polling
If your server was down or a webhook was missed, you can check order status directly:
GET /v2/checkout/{checkout_id}
See the endpoint reference →