Skip to content

Webhooks Integration Guide

Gift Card Hero sends HTTP POST requests to your server when specific gift card events occur. Use webhooks to keep external systems in sync with gift card activity in real time.


Setup

  1. Go to For developers in the left navigation
  2. Tick Enable REST API and save. Webhook settings appear only while the REST API is on
  3. Click Generate Secret to create a webhook signing secret

Webhook secret generation

The secret is shown only once

Copy the secret and store it securely. If you lose it, click Regenerate Secret to create a new one. The old secret stops working at once, so update your server too.

  1. Enter your endpoint URLs for each webhook type you want to receive, then click Save in the save bar

Webhook endpoint URLs

A new or changed endpoint URL must start with https:// and point to a server reachable from the internet. Addresses inside a private network — localhost, 127.0.0.1, 10.x.x.x, 192.168.x.x and similar — are refused when you save. A URL that was saved before this rule keeps working until you change it, but it is still only called if it points to a public address. To test against a local server, expose it through a tunnel such as ngrok and use the public https URL it gives you.


Event types

Gift Card Hero has three webhook URL fields:

Event Topic header value When it fires
Gift card order gift_card_order Gift Card Hero processes the gift cards of an order. The payload lists the cards that belong to the order
Gift card payment gift_card_payment An order is created that was paid, fully or partly, with a gift card
Gift card balance change gift_card_balance Not sent at the moment

Balance change webhook

The Gift card balance webhook URL field is on the page, but Gift Card Hero does not send this event yet. To follow balance changes today, poll GET /gift-cards/transactions or use the gift_card_payment event for payments.

Webhooks are sent only while Enable REST API is on and the URL for that event is filled in.


Delivery

  • Method: POST
  • Timeout: 6 seconds
  • Attempts: Up to 5 delivery attempts in total (the first one and up to 4 retries), with exponentially growing delays that start at roughly 30 to 60 seconds
  • Redirects: Not followed. A 3xx response counts as a failed delivery, so use the final URL of your endpoint.

If your endpoint does not return a 2xx response within 6 seconds, the delivery is considered failed and will be retried.


Headers

Every webhook request includes the following headers:

Header Value
Content-Type application/json
User-Agent GiftHero-Webhooks/1.0
X-GiftHero-Topic The event type (e.g., gift_card_order)
X-GiftHero-Signature HMAC-SHA256 signature for verification. Sent only when you have generated a webhook secret

Signature verification

The X-GiftHero-Signature header contains an HMAC-SHA256 signature, hex-encoded and prefixed with sha256=.

Important: The signature is computed over a JSON object containing only { type, timestamp } from the payload body — NOT the entire request body.

Verification steps

  1. Parse the request body and extract the type and timestamp fields
  2. Construct the signing payload: JSON.stringify({ type, timestamp })
  3. Compute the HMAC-SHA256 using your webhook secret as the key
  4. Hex-encode the result and prefix with sha256=
  5. Use a constant-time comparison to compare with the X-GiftHero-Signature header

Example (Node.js)

const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const signingPayload = JSON.stringify({
    type: body.type,
    timestamp: body.timestamp
  });

  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(signingPayload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Payload schemas

Every request body has the same envelope. The event details are inside data:

{
  "type": "gift_card_order",
  "timestamp": "2026-03-15T10:30:00.000Z",
  "data": { }
}

Gift card order (gift_card_order)

{
  "type": "gift_card_order",
  "timestamp": "2026-03-15T10:30:00.000Z",
  "data": {
    "order": {
      "id": 5551234567890,
      "name": "#1042",
      "email": "buyer@example.com",
      "createdAt": "2026-03-15T10:30:00.000Z",
      "lineItems": [
        {
          "id": 11198765432100,
          "title": "Gift Card",
          "quantity": 1,
          "sku": "GC-50",
          "variantId": 44556677
        }
      ]
    },
    "giftCards": [
      {
        "id": 987654321,
        "balance": "50.00",
        "currency": "USD",
        "lastCharacters": "ab12",
        "initialValue": "50.00",
        "customer": { "id": 123, "email": "buyer@example.com", "name": "Alex Buyer" },
        "recipient": { "id": 456, "email": "friend@example.com", "name": "Sam Friend" },
        "code": "abcd1234efgh5678",
        "lineItemId": 11198765432100,
        "sku": "GC-50"
      }
    ]
  }
}

Gift card payment (gift_card_payment)

Each entry in giftCards also carries the transaction that paid for the order.

{
  "type": "gift_card_payment",
  "timestamp": "2026-03-16T14:22:00.000Z",
  "data": {
    "order": {
      "id": 5551234567891,
      "name": "#1043",
      "email": "shopper@example.com",
      "createdAt": "2026-03-16T14:22:00.000Z",
      "lineItems": [
        { "id": 11198765432200, "title": "Running Shoes", "quantity": 1, "sku": "SHOE-9" }
      ]
    },
    "giftCards": [
      {
        "id": 987654321,
        "balance": "0.00",
        "currency": "USD",
        "lastCharacters": "ab12",
        "initialValue": "50.00",
        "customer": null,
        "recipient": null,
        "transaction": {
          "id": "gid://shopify/OrderTransaction/123456789",
          "amount": "50.00",
          "currency": "USD",
          "type": "debit",
          "createdAt": "2026-03-16T14:22:00.000Z"
        }
      }
    ]
  }
}

Field notes

  • data.order and data.giftCards are the fields to build on. data also repeats some order fields at its top level (orderId, orderName, orderEmail, createdAt, lineItems) and carries internal fields (storeId, shop, jobType). Do not rely on those.
  • balance is the card's balance when the webhook is built, not when the order was placed.
  • code is the full gift card code. It is included only when Gift Card Hero has the code on record, and omitted otherwise. Treat it like a password and make sure your endpoint is secured.
  • lineItemId and sku are included when the card can be matched to an order line.
  • lineItems entries carry id, title, quantity, sku and variantId (no price).
  • If a card's details cannot be loaded, its entry is reduced to id, zero amounts and an empty lastCharacters. Re-read the card with the REST API when you see that.

Best practices

  • Return 2xx quickly — acknowledge the webhook immediately and process the payload asynchronously. Responses taking longer than 6 seconds are treated as failures.
  • Handle duplicates — the same event may be delivered more than once due to retries. Use the order and gift card IDs in the event data to deduplicate. The timestamp is the time of each attempt, so it is not a stable key.
  • Verify HMAC signatures — always verify the X-GiftHero-Signature header before processing any webhook payload. Reject requests with invalid signatures.
  • Use idempotency keys — design your webhook handler to be idempotent so that processing the same event twice produces the same result.
  • Monitor failures — if your endpoint is consistently failing, Gift Card Hero stops retrying after 5 attempts in total. Check your server logs and endpoint health regularly.