Choose this when
Use this path when your product needs signed Lamba events for auth, membership, session, webhook, campaign, loyalty, notification, or audit workflows.
Webhook handlers should be small:
- read the raw request body
- verify the signature
- deduplicate by event ID
- persist the event or enqueue work
- return
2xx
Before you start
You need:
- a public HTTPS endpoint under your control
- raw-body access in your web framework
- durable storage for processed event IDs
- access to the selected Workspace, Project, and environment in the Lamba console
Get credentials from Console
Open the selected Project, then go to Integration > Webhooks.
| Value | Console source | Env var | Used for |
|---|---|---|---|
| Endpoint URL | Integration > Webhooks > Add endpoint | LAMBA_WEBHOOK_ENDPOINT_URL | Public HTTPS URL that receives `POST` deliveries |
| Subscribed events | Integration > Webhooks > Add endpoint or configured endpoint row | LAMBA_WEBHOOK_EVENTS | Limits delivery to the event families your product handles |
| Signing secretSecret | Integration > Webhooks > Open security | LAMBA_WEBHOOK_SECRET | HMAC verification for every delivery |
| Endpoint ID | Integration > Webhooks > Configured endpoints | LAMBA_WEBHOOK_ENDPOINT_ID | Delivery inspection, retry workflows, and endpoint-specific operations |
Configure environment variables
LAMBA_WEBHOOK_SECRET=<signing-secret>
LAMBA_WEBHOOK_TOLERANCE_SECONDS=300
Store the secret in server-side secret storage. Rotate it from the console if it is exposed.
Make the first request
Register your endpoint from the console, then use Send sandbox on the configured endpoint row. The delivery is a POST to your URL.
POST
Your HTTPS receiver/api/lamba/webhooks- Auth
- HMAC signature in Lamba delivery headers
- Used for
- Receives signed Lamba event facts
Minimal Next.js route:
import crypto from "node:crypto";
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const rawBody = await request.text();
const signature = request.headers.get("x-lamba-signature") ?? "";
const timestamp = request.headers.get("x-lamba-timestamp") ?? "";
if (!verifySignature(rawBody, signature, timestamp, process.env.LAMBA_WEBHOOK_SECRET!)) {
return NextResponse.json({ error: "invalid_signature" }, { status: 400 });
}
const event = JSON.parse(rawBody) as { id: string; type: string };
await persistEventForAsyncProcessing(event.id, rawBody);
return new Response(null, { status: 204 });
}
function verifySignature(rawBody: string, signature: string, timestamp: string, secret: string) {
const expected = `v1=${crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex")}`;
return crypto.timingSafeEqual(
Buffer.from(expected, "utf8"),
Buffer.from(signature, "utf8")
);
}
async function persistEventForAsyncProcessing(_eventId: string, _rawBody: string) {
// Store the event ID before doing side effects so retries stay idempotent.
}
Request fields
Delivery headers:
| Field | Type | Required | Meaning | Notes |
|---|---|---|---|---|
X-Lamba-Signature | string | Required | HMAC signature over `timestamp.rawBody`. | Compare with constant-time equality. |
X-Lamba-Timestamp | Unix timestamp or ISO timestamp | Required | Delivery timestamp used for replay protection. | Reject requests outside your tolerance window. |
X-Lamba-Event | string | Required | Event type, such as `user.created` or `campaign.sent`. | - |
X-Lamba-Delivery | string | Required | Unique delivery attempt identifier. | Use the body `id` as the event idempotency key. |
Event body:
{
"id": "evt_...",
"type": "user.created",
"workspaceId": "wrk_...",
"projectId": "prj_...",
"environment": "test",
"createdAt": "2026-05-31T12:00:00Z",
"data": {
"userId": "usr_...",
"email": "customer@example.com"
}
}
| Field | Type | Required | Meaning | Notes |
|---|---|---|---|---|
id | string | Required | Stable event ID. | Store this value to deduplicate retries. |
type | string | Required | Event type that determines which handler should run. | - |
workspaceId | string | Required | Workspace where the event occurred. | Use this instead of customer-facing tenant terminology. |
projectId | string | Required | Project runtime context for the event. | - |
environment | test | prod | Required | Environment that produced the event. | - |
createdAt | ISO-8601 datetime | Required | When Lamba recorded the event fact. | - |
data | object | Required | Event-specific payload. | Do not assume every event has the same nested fields. |
Response fields
Your receiver controls the HTTP response:
| Field | Type | Required | Meaning | Notes |
|---|---|---|---|---|
2xx | HTTP status | Required | Acknowledges durable receipt. | Return only after signature verification and event persistence. |
4xx | HTTP status | Optional | Permanent receiver-side rejection. | Use for invalid signature or malformed requests. |
5xx or timeout | HTTP status / network failure | Optional | Temporary receiver failure. | Lamba retries failed deliveries with backoff. |
Done when
Done when
- The endpoint is registered under `Integration > Webhooks` for the selected environment.
- Your receiver verifies `X-Lamba-Signature` against the raw body.
- Replay protection rejects stale timestamps.
- The event body `id` is stored before side effects run.
- A sandbox delivery returns `2xx` and appears as successful in delivery logs.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Invalid signature | Framework parsed or reformatted the body before verification | Verify against the raw request body exactly as received |
| Repeated retries | Handler returns non-2xx, times out, or persists after side effects | Persist first, enqueue work, then return quickly |
| Duplicate downstream action | Event ID is not used as idempotency key | Store body id and treat repeat deliveries as already processed |
| Missing events | Endpoint subscription filters exclude the event type | Review Integration > Webhooks subscribed events |
| Sandbox events appear in production handling | Environment not checked | Branch on body environment and keep secrets separate |
Related docs
- Webhook Signature Verification:
/docs/reference/webhook-signatures - Webhook Events:
/docs/reference/webhook-events - Webhook Deliveries:
/docs/reference/webhook-deliveries - Incident Communication:
/docs/reference/incident-comms