Callbacks

Verify backend events safely.

Human Gateway callbacks let your backend receive signed server-to-server events after Verify or Auth flows complete.

What callbacks are

A callback is an HTTP POST request sent by Human Gateway to a backend URL configured by the partner. The callback contains event metadata, a JSON payload, timestamp headers, and a signature.

Use callbacks as your backend source of truth. Frontend SDK results are useful for immediate UX, but durable access decisions and database updates should happen only after your backend verifies a signed callback.

Supported events

Human Gateway currently sends callbacks for completed Verify and Auth flows.

  • verification.completed— a user completed HG Verify.
  • auth.completed— a user completed HG Auth and a partner-scoped auth session was created.

Callback headers

Human Gateway sends signed metadata headers with each callback. Your backend should validate them before trusting the payload.

Callback headers

content-type: application/json
x-hg-event-id: evt_xxx
x-hg-event: verification.completed
x-hg-project-id: project_xxx
x-hg-timestamp: 1780249541000
x-hg-signature: hex_signature

verification.completed payload

This event confirms that a user completed Verify successfully for a project/action context. It does not create a Human Gateway auth session.

verification.completed

{
  "event_id": "evt_xxx",
  "event": "verification.completed",
  "success": true,
  "verified_human": true,
  "verification_level": "orb",
  "unique_for_partner": true,
  "already_verified": false,
  "project_id": "project_xxx",
  "action": "hg_verify_action_key_xxx",
  "created_at": "2026-07-01T00:00:00.000Z"
}

auth.completed payload

This event confirms that a user completed Auth successfully. It includes a partner-scoped user reference and Human Gateway auth session reference.

auth.completed

{
  "event_id": "evt_xxx",
  "event": "auth.completed",
  "success": true,
  "verified_human": true,
  "verification_level": "orb",
  "project_id": "project_xxx",
  "partner_id": "partner_xxx",
  "partner_user_id": "puser_xxx",
  "auth_session_id": "authsess_xxx",
  "action": "hg_auth_action_key_xxx",
  "expires_at": "2026-08-01T00:00:00.000Z",
  "created_at": "2026-07-01T00:00:00.000Z"
}

Signature model

Human Gateway signs callbacks with HMAC SHA-256 using the project callback secret. The signature is computed over the timestamp and the exact raw request body.

Signature formula

signed_message = "${x-hg-timestamp}.${rawBody}"
x-hg-signature = HMAC_SHA256(callback_secret, signed_message)

The callback secret is a backend secret. It must never be exposed in frontend code, public repositories, screenshots, client-side logs, or variables using a NEXT_PUBLIC_ prefix.

Use the raw body

Callback signatures must be verified against the exact raw request body. Parsing and re-stringifying JSON before verification can change formatting and cause signature validation to fail.

Raw body handling

// Correct: verify the exact raw request body.
const body = await request.text();

// Incorrect: parsing JSON before verification can break signatures.
const payload = await request.json();

Next.js App Router example

In a Next.js App Router project, create a backend route such as app/api/human-gateway/callback/route.ts. The endpoint should read the raw body, verify the Human Gateway callback, validate the expected project, and process events idempotently.

Callback endpoint

import { NextResponse } from "next/server";
import {
  HumanGatewayCallbackError,
  verifyCallback,
} from "@human-gateway/sdk/server";

export async function POST(request: Request): Promise<Response> {
  const callbackSecret = process.env.HG_CALLBACK_SECRET;
  const expectedProjectId = process.env.HG_PROJECT_ID;

  if (!callbackSecret || !expectedProjectId) {
    return NextResponse.json(
      {
        success: false,
        error: "missing_human_gateway_config",
      },
      { status: 500 }
    );
  }

  const body = await request.text();

  try {
    const callback = verifyCallback({
      body,
      headers: request.headers,
      secret: callbackSecret,
      expectedProjectId,
    });

    // Store callback.event_id and process it idempotently.
    // If this event_id was already processed, return 200
    // without repeating business logic.

    if (callback.event === "verification.completed") {
      const payload = callback.payload;

      if (payload.verified_human === true) {
        // Persist verified state in your backend.
      }
    }

    if (callback.event === "auth.completed") {
      const payload = callback.payload;

      if (payload.verified_human === true) {
        // Persist or reconcile auth state in your backend.
      }
    }

    return NextResponse.json({
      success: true,
      received: true,
      event_id: callback.event_id,
      event: callback.event,
      project_id: callback.project_id,
    });
  } catch (error) {
    if (error instanceof HumanGatewayCallbackError) {
      return NextResponse.json(
        {
          success: false,
          error: error.code,
        },
        { status: 401 }
      );
    }

    return NextResponse.json(
      {
        success: false,
        error: "callback_error",
      },
      { status: 500 }
    );
  }
}

Backend environment variables

HG_PROJECT_ID=project_xxx
HG_CALLBACK_SECRET=your_callback_secret

Idempotency

Partners should treat callbacks as at-least-once delivery. The same event may be delivered more than once during retries or network uncertainty. Store event_id and make processing idempotent.

Idempotency flow

1. Receive callback.
2. Verify signature and timestamp.
3. Check whether event_id already exists.
4. If event_id exists, return 200 without repeating work.
5. If event_id is new, store it.
6. Process the business action.
7. Mark the event as processed.
8. Return 200.

Expected response

Return a successful 2xx response only after your backend accepts the callback. If the callback is invalid, return an error status and do not process the event.

A minimal successful response can be:

Successful response

{
  "success": true,
  "received": true
}

Security checklist

Callback security checklist

Security checklist
- Use HTTPS callback URLs in production
- Keep HG_CALLBACK_SECRET only on the backend
- Never expose callback secrets with NEXT_PUBLIC_
- Read the raw body before parsing JSON
- Verify x-hg-signature
- Validate x-hg-timestamp freshness
- Validate expectedProjectId
- Store event_id for idempotency
- Return 2xx only after accepting the callback
- Rotate the callback secret if it may be exposed

Privacy model

Human Gateway callbacks are designed to avoid exposing raw World ID nullifiers, raw World ID proofs, biometric data, identity documents, government identity data, or unnecessary personal identity information.

Partners may receive operational fields such as event_id, event, project_id, verified_human, partner_user_id, auth_session_id, and timestamps depending on the event.

verified_human, verification_level, partner_user_id, auth_session_id, and timestamps depending on the event.

verification_level can be device, document, secure_document, orb, or null. Human Gateway only includes the level returned by World ID.

Next steps

Continue with the SDK guide for frontend Verify/Auth usage, SDK options, and error handling.