GridlinePOS Solutions

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

EventDescription
order.createdFired when a new order is created
order.paidFired when payment is captured for an order
order.refundedFired when a full or partial refund is processed
customer.createdFired when a new customer profile is created
customer.updatedFired when customer details are modified
points.earnedFired when loyalty points are awarded to a customer
points.redeemedFired when loyalty points are redeemed for a reward
product.createdFired when a new product is added to the catalog
product.updatedFired 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 body
  • X-Gridline-TimestampUnix timestamp when the event was sent
  • X-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:

AttemptDelayNotes
1st retry30 secondsImmediate retry for transient failures
2nd retry5 minutesShort backoff
3rd retry30 minutesMedium backoff
4th retry2 hoursLong backoff
5th retry24 hoursFinal attempt, then marked as failed
  • Your endpoint must respond within 30 seconds or the delivery is considered failed
  • Return a 200 status to acknowledge receipt (any 2xx works)
  • Use the X-Gridline-Event-Id header 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

  1. Go to Dashboard > Settings > Webhooks
  2. Click Add Endpoint
  3. Enter your HTTPS endpoint URL
  4. Select which events you want to receive
  5. Copy the signing secret (used for signature verification)
  6. Test with the "Send Test Event" button