Layers
Partner APIAPI referenceWebhooks

POST /v1/webhook-endpoints

Register a new webhook endpoint. Returns the signing secret once - store it immediately.

View as Markdown
POST/v1/webhook-endpoints
Phase 1stableidempotent
Auth
Bearer

Creates a new webhook subscription under the calling org. Pass projectId to narrow to a single project (events unrelated to that project are dropped at emit time); omit it for an org-wide endpoint.

The response carries a signingSecret shown exactly once. Store it in your secrets manager before the response closes - we cannot display it again. Use it to verify every incoming delivery via the signature helper in the webhooks overview.

Headers
  • Idempotency-Key
    string (UUID)optional
    Replays within the idempotency window return the original response.
Body
  • url
    stringrequired
    HTTPS URL. http://localhost is accepted in dev only.
  • events
    string[]required
    1–50 event types from the catalog. See overview for the full list.
  • description
    stringoptional
    Human label shown in the admin UI + delivery logs.
  • metadata
    objectoptional
    Optional opaque key/value pairs for your own bookkeeping (Stripe-style). Returned on reads; Layers never reads or indexes it.
  • projectId
    string (UUID)optional
    If set, only events for this project are delivered.
  • scope
    "own" | "all_children"optional
    Delivery scope. own (default) delivers only this org's events. all_children is the firehose — this org's events PLUS every direct child's, each tagged with data.organizationId. Setting all_children requires an org:admin key.

Firehose (scope: "all_children"). If you run customers as sub-organizations, one parent endpoint can receive every child's events instead of registering an endpoint per child. Each delivery carries data.organizationId so you can attribute it to the right customer. Only a parent org:admin key may open one; a non-admin key requesting it gets 403 FORBIDDEN_SCOPE.

Example request

curl -X POST https://api.layers.com/v1/webhook-endpoints \
  -H "Authorization: Bearer $LAYERS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/layers/webhooks",
    "events": ["job.completed", "job.failed", "content.approved"],
    "description": "prod reconciler"
  }'
import { randomUUID } from "node:crypto";

const res = await fetch("https://api.layers.com/v1/webhook-endpoints", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.LAYERS_API_KEY}`,
    "Idempotency-Key": randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://your-app.example.com/layers/webhooks",
    events: ["job.completed", "job.failed", "content.approved"],
    description: "prod reconciler",
  }),
});
const { endpoint, signingSecret } = await res.json();
// Persist `signingSecret` to your secret store immediately.
import os, uuid, requests

res = requests.post(
    "https://api.layers.com/v1/webhook-endpoints",
    headers={
        "Authorization": f"Bearer {os.environ[\'LAYERS_API_KEY\']}",
        "Idempotency-Key": str(uuid.uuid4()),
        "Content-Type": "application/json",
    },
    json={
        "url": "https://your-app.example.com/layers/webhooks",
        "events": ["job.completed", "job.failed", "content.approved"],
        "description": "prod reconciler",
    },
)
body = res.json()
# Persist body["signingSecret"] to your secret store immediately.

Response

201Created - signing secret returned once
{
  "endpoint": {
    "id": "d4c71b62-7f08-4dc9-9d2c-8f7e2b9c4411",
    "organizationId": "org_2481fa5c-a404-44ed-a561-565392499abc",
    "projectId": null,
    "url": "https://your-app.example.com/layers/webhooks",
    "description": "prod reconciler",
    "status": "active",
    "scope": "own",
    "events": ["job.completed", "job.failed", "content.approved"],
    "apiVersion": "v1",
    "createdAt": "2026-04-20T18:28:00.000Z",
    "updatedAt": "2026-04-20T18:28:00.000Z",
    "lastSuccessAt": null,
    "lastFailureAt": null,
    "consecutiveFailureCount": 0
  },
  "signingSecret": "whsec_s3cret...",
  "warning": "Signing secret shown once. Store it securely - rotate via POST /v1/webhook-endpoints/:id/rotate-secret if compromised."
}

Errors

StatusCodeWhen
422VALIDATIONURL invalid, events empty, unknown event type, >50 events.
404NOT_FOUNDprojectId doesn't belong to the calling org.
403FORBIDDEN_SCOPEscope: "all_children" requested without an org:admin key.
409IDEMPOTENCY_CONFLICTIdempotency-Key reused with a different body.

See also

On this page