Webhooks
Webhooks allow your application to receive real-time notifications when events happen in your Gridline account. Instead of polling the API, you register a URL and Gridline pushes events to you.
Available Events
| Event | Description |
|---|---|
order.created | Fired when a new order is created |
order.paid | Fired when payment is captured for an order |
order.refunded | Fired when a full or partial refund is processed |
customer.created | Fired when a new customer profile is created |
customer.updated | Fired when customer details are modified |
points.earned | Fired when loyalty points are awarded to a customer |
points.redeemed | Fired when loyalty points are redeemed for a reward |
product.created | Fired when a new product is added to the catalog |
product.updated | Fired when product details or stock changes |
Webhook Payload Structure
All webhooks are delivered as HTTP POST requests with a JSON body following this structure:
Webhook Payload
{
"id": "evt_a1b2c3d4e5",
"type": "order.paid",
"created_at": "2024-01-15T10:30:45Z",
"merchant_id": "mer_xyz789",
"data": {
"id": "ord_a1b2c3d4",
"status": "paid",
"total": 2499,
"currency": "CAD",
"customer_id": "cus_xyz789",
"items_count": 3
}
}Signature Verification
Every webhook includes a signature header to verify the payload originated from Gridline. Always verify signatures before processing events.
Headers Included
X-Gridline-SignatureHMAC-SHA256 signature of the request bodyX-Gridline-TimestampUnix timestamp when the event was sentX-Gridline-Event-IdUnique event ID for idempotency
verify-webhook.tsTypeScript
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
timestamp: string,
secret: string
): boolean {
// Construct the signed content
const signedContent = `${timestamp}.${payload}`;
// Compute expected signature
const expected = crypto
.createHmac('sha256', secret)
.update(signedContent)
.digest('hex');
// Timing-safe comparison
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get('X-Gridline-Signature')!;
const timestamp = request.headers.get('X-Gridline-Timestamp')!;
// Reject events older than 5 minutes (replay protection)
const age = Date.now() / 1000 - parseInt(timestamp);
if (age > 300) {
return new Response('Event too old', { status: 400 });
}
if (!verifyWebhookSignature(body, signature, timestamp, process.env.WEBHOOK_SECRET!)) {
return new Response('Invalid signature', { status: 401 });
}
const event = JSON.parse(body);
// Process the event...
return new Response('OK', { status: 200 });
}Retry Policy
If your endpoint returns a non-2xx status code or times out, Gridline will retry the delivery with exponential backoff:
| Attempt | Delay | Notes |
|---|---|---|
| 1st retry | 30 seconds | Immediate retry for transient failures |
| 2nd retry | 5 minutes | Short backoff |
| 3rd retry | 30 minutes | Medium backoff |
| 4th retry | 2 hours | Long backoff |
| 5th retry | 24 hours | Final attempt, then marked as failed |
- Your endpoint must respond within 30 seconds or the delivery is considered failed
- Return a
200status to acknowledge receipt (any 2xx works) - Use the
X-Gridline-Event-Idheader for idempotent processing
Example Payloads
order.paid
POST to your endpoint
{
"id": "evt_order_paid_001",
"type": "order.paid",
"created_at": "2024-01-15T10:30:45Z",
"merchant_id": "mer_xyz789",
"data": {
"id": "ord_a1b2c3d4",
"status": "paid",
"total": 2499,
"currency": "CAD",
"customer_id": "cus_xyz789",
"payment": {
"method": "card",
"card_last4": "4242"
},
"items": [
{
"product_id": "prod_abc123",
"name": "Flat White",
"quantity": 2,
"unit_price": 550
}
]
}
}points.earned
POST to your endpoint
{
"id": "evt_points_earned_001",
"type": "points.earned",
"created_at": "2024-01-15T10:31:00Z",
"merchant_id": "mer_xyz789",
"data": {
"customer_id": "cus_xyz789",
"points_earned": 25,
"balance_after": 350,
"order_id": "ord_a1b2c3d4",
"earn_rate": "1 point per $1"
}
}customer.created
POST to your endpoint
{
"id": "evt_customer_created_001",
"type": "customer.created",
"created_at": "2024-01-15T09:00:00Z",
"merchant_id": "mer_xyz789",
"data": {
"id": "cus_xyz789",
"name": "Jordan Smith",
"email": "jordan@example.com",
"phone": "+1-555-0123",
"created_at": "2024-01-15T09:00:00Z"
}
}Setting Up Webhooks
- Go to
Dashboard > Settings > Webhooks - Click Add Endpoint
- Enter your HTTPS endpoint URL
- Select which events you want to receive
- Copy the signing secret (used for signature verification)
- Test with the "Send Test Event" button