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 verifies three signed callback event types. Verify and Auth events represent completed product flows, while callback.test is an operational delivery test.
verification.completed— a user completed HG Verify.auth.completed— a user completed HG Auth and a Human Gateway auth session was created for the Project-scoped PartnerUser.callback.test— a signed operational event generated from Partner Console to validate callback delivery.
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_signatureverification.completed payload
This event confirms that a user completed Verify successfully for a specific Project and Action context. It does not create a Human Gateway auth session or PartnerUser identity.
verification.completed
{
"event_id": "evt_xxx",
"event": "verification.completed",
"success": true,
"verified_human": true,
"unique_for_action": 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 the Project-scoped partner_user_id and the Human Gateway auth_session_id created for that Auth flow.
auth.completed
{
"event_id": "evt_xxx",
"event": "auth.completed",
"success": true,
"verified_human": true,
"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"
}callback.test payload
This operational event is generated when callback delivery is tested from Partner Console. It confirms transport and signature handling only; it does not represent a completed Verify or Auth flow.
callback.test
{
"event_id": "evt_xxx",
"event": "callback.test",
"success": true,
"test": true,
"project_id": "project_xxx",
"partner_id": "partner_xxx",
"message": "Human Gateway callback test delivery.",
"triggered_by": "partner_user_xxx",
"triggered_by_role": "admin",
"created_at": "2026-07-01T00:00:00.000Z"
}Typed callback verification
The server SDK's verifyCallback() validates the signed callback envelope, timestamp, optional expected Project binding, and required payload contract for the supported event. After narrowing on callback.event, TypeScript also narrows callback.payload to the matching event payload type.
verifyCallbackSignature() is a lower-level cryptographic and timestamp helper. A valid signature alone does not establish the complete callback contract or bind the event to the Project your endpoint expects. Prefer verifyCallback() for normal application callback handling.
Frontend results returned by hg.verify() or hg.auth() are separate from signed backend callback payloads. Treat frontend results as immediate UX signals and reconcile durable backend state from verified callbacks.
Signature model
Human Gateway signs callbacks with HMAC SHA-256 using the Project callback secret. The secret belongs to the Project, not to an individual Action. 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 Project callback secret is a backend secret. It authenticates callback deliveries for that Project regardless of which Action produced the event. 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 using the trusted Project ID and matching callback secret, 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;
// payload is validated and typed as verification.completed.
// Persist verified state for payload.action.
}
if (callback.event === "auth.completed") {
const payload = callback.payload;
// payload is validated and typed as auth.completed.
// Reconcile payload.partner_user_id and payload.auth_session_id.
}
if (callback.event === "callback.test") {
const payload = callback.payload;
// Operational test event. Do not treat it as a Verify/Auth completion.
console.log(payload.message);
}
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_secretHG_PROJECT_ID and HG_CALLBACK_SECRET are simple local names for a single-Project integration. If your backend receives callbacks for multiple Projects, namespace each trusted Project ID and callback secret pair locally, for example HG_COMMUNITY_PROJECT_ID with HG_COMMUNITY_CALLBACK_SECRET.
Do not read an unverified payload.project_id first and use it to choose a callback secret. Select the trusted Project configuration from your own endpoint or server-side routing context, then verify the callback against that Project.
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 Project callback secrets only on the backend
- Never expose callback secrets with NEXT_PUBLIC_
- Keep each trusted Project ID paired with its callback secret
- Never select a secret from an unverified payload.project_id
- Read the raw body before parsing JSON
- Verify x-hg-signature
- Validate x-hg-timestamp freshness
- Validate expectedProjectId
- Validate the callback event and payload contract
- Store event_id for idempotency
- Return 2xx only after accepting the callback
- Rotate the Project callback secret if it may be exposedPrivacy 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.
Verification callbacks include scoped Verify fields such as verified_human, unique_for_action, already_verified, Project and Action context, and event metadata. Auth callbacks have their own identity and session fields.
Auth callbacks expose Human Gateway abstractions such as the Project-scoped partner_user_id and the auth_session_id. They do not expose the underlying World session identifier, internal World identity references, or proof internals.
Next steps
Continue with the SDK guide for frontend Verify/Auth usage, SDK options, and error handling.