Skip to main content

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

  1. Customer completes payment on Gale’s checkout page
  2. Gale processes the payment
  3. Gale sends a POST request to your webhook endpoint with event data
  4. Your server processes the event (e.g., fulfills order, sends email)
  5. Your server returns a 200 OK response
  6. 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

  1. Log in to Gale Dashboard
  2. Go to SettingsWebhooks
  3. Click Add Endpoint
  4. Enter your webhook URL
  5. Select the events to subscribe to
  6. 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:
AttemptDelay after previous failure
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry6 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

EventWhen it fires
order.completedPayment captured successfully
order.failedPayment failed or declined
refund.createdRefund initiated by merchant
refund.succeededRefund processed successfully
refund.failedRefund 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

ValueMeaning
completedPayment captured, ready to fulfill
failedPayment failed
cancelledOrder cancelled

payment_status — Payment state

ValueMeaning
capturedPayment successfully captured
failedPayment failed or declined
refundedFully refunded
partially_refundedPartially 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 →