Horizon

Test Hygraph webhooks locally

Receive Hygraph webhook events on localhost with a Horizon tunnel, and verify the gcms-signature header.

Receive Hygraph events on your laptop while you build, with a URL Hygraph can reach.

Before you begin

  • Node.js 18 or later
  • A Horizon account and the CLI (see Getting started)
  • A reserved subdomain for -s. Reserve one on the Subdomains page.
  • A Hygraph project where you can manage webhooks

Start your app

When a webhook has a secret key, Hygraph sends a gcms-signature header. It looks like sign=<signature>, env=<environment>, t=<timestamp>. The signature is an HMAC SHA-256 (a keyed hash) of a JSON payload built from the body, the environment name and the timestamp.

Hygraph's @hygraph/utils package does that work. Install it.

npm install @hygraph/utils

Read the raw body with request.text() and pass it in unchanged.

app/api/webhooks/hygraph/route.ts
import { verifyWebhookSignature } from "@hygraph/utils";

export async function POST(request: Request) {
  const secret = process.env.HYGRAPH_WEBHOOK_SECRET;
  if (!secret) {
    return new Response("Missing HYGRAPH_WEBHOOK_SECRET", { status: 500 });
  }

  const body = await request.text();
  const signature = request.headers.get("gcms-signature") ?? "";

  const isValid = verifyWebhookSignature({ body, signature, secret });
  if (!isValid) {
    return new Response("Invalid signature", { status: 401 });
  }

  const { operation, data } = JSON.parse(body);
  console.log(`Received Hygraph ${operation} for ${data.__typename}`);

  return new Response("ok", { status: 200 });
}

Pick a secret and store it in an environment variable. Use a random, high-entropy string.

.env.local
HYGRAPH_WEBHOOK_SECRET=replace-with-a-long-random-string

Start the app on port 3000.

npm run dev

Start a tunnel

Use -s with a subdomain you reserved. Without it, the subdomain is random and changes every run, so your Hygraph webhook would point at a dead URL after a restart. Reserved subdomains are a paid feature, see Pricing.

hrzn tunnel http://localhost:3000 -s my-app
Output
HORIZON: Tunnel connected
  URL          https://my-app.hrzn.run (reserved)
  Forwarding   http://localhost:3000
  Request log  https://hrzn.run/dashboard/tunnels/my-app

Your public URL is https://my-app.hrzn.run. Keep this terminal open.

Add the webhook in Hygraph

  1. Open Project Settings, then Automation, then Webhooks.
  2. Select Add webhook.
  3. Enter a name and a description.
  4. Turn on the option to include the payload.
  5. Enter https://my-app.hrzn.run/api/webhooks/hygraph as the URL.
  6. Select the content model, the stage and the action you want to listen for.
  7. Enter the same value as HYGRAPH_WEBHOOK_SECRET in the secret key field.
  8. Select Add webhook.

Trigger an event

Hygraph's docs describe no test button and no resend. Trigger a real event instead: create, update or publish an entry of the content model you selected.

To inspect a call, select View logs next to the webhook. Each entry shows the timestamp, the HTTP status, the content model, the action and the duration. Select an entry to see the request and response. Hygraph keeps logs for 7 days.

Check it works

Publish an entry. Horizon prints one line for the request:

Output
  POST    200  /api/webhooks/hygraph

Your app terminal prints the operation and the model name, for example:

Output
Received Hygraph publish for Post

If the line shows [401], see Troubleshooting.

Troubleshooting

The signature doesn't match

  • Check that HYGRAPH_WEBHOOK_SECRET is identical to the secret key on the webhook, with no extra spaces or newline.
  • Pass the raw body to verifyWebhookSignature. Don't run JSON.parse and JSON.stringify first, because that can change the bytes.
  • Restart npm run dev after you edit .env.local.

The handler returns 401 and gcms-signature is empty

Hygraph only sends the header when the webhook has a secret key. Edit the webhook and add one.

The URL changed after a restart

You started the tunnel without -s, so Horizon gave you a new random subdomain. Restart with -s my-app and the URL stays the same. -s needs a subdomain you reserved, see Pricing.

Nothing reaches your app

  • Check that the Horizon terminal is still running. If its last line is Connection lost. Reconnecting…, wait for Reconnected.
  • Check that the webhook URL ends with /api/webhooks/hygraph.
  • Open View logs to see whether Hygraph sent the call.

Next steps

On this page