---
title: Webhooks
description: Signed webhook deliveries with retries, a delivery log and replay - Standard Webhooks signatures and CloudEvents bodies.
type: tutorial
product: webhooks
summary: What a webhook endpoint is, what you receive, and where to go next.
updated: 2026-10-01
order: 0
---

A [Webhook Endpoint](/docs/api/webhook_endpoints) is an HTTPS address of yours
that Sylphx Events delivers an environment's events to. Every delivery is
signed, retried on a fixed schedule, recorded in a delivery log, and can be
replayed after an outage.

## What a delivery is

- **A CloudEvents document.** The body is one event, `specversion` `1.0`, as
  JSON, at most 1 MiB. See [event types](/docs/webhooks/event-types).
- **Signed with Standard Webhooks.** Each attempt carries `webhook-id`,
  `webhook-timestamp` and `webhook-signature`, so any
  [Standard Webhooks](https://www.standardwebhooks.com) library verifies it.
  [Receiving webhooks](/docs/webhooks/receiving) has the verification steps and
  a complete receiver.
- **At least once.** A timeout cannot tell Events whether your receiver
  finished, so a delivery can arrive twice with the same `webhook-id`. Make the
  effect idempotent on it.
- **Retried, then dead-lettered.** Eight attempts over about 27.6 hours; what
  is left is dead-lettered, kept, and replayable.

## Create an endpoint

The URL must be `https` and public. `spec.event_types` chooses what the
endpoint receives: an exact type, a prefix ending in `.*`, or `*` for every type
(the default).

<CodeTabs>
	<CodeTab
		label="CLI"
		language="bash"
		code={`sylphx events webhook-endpoints create \\
  --parent orgs/acme/projects/shop/envs/production \\
  --spec.uri https://example.com/webhooks`}
	/>
	<CodeTab
		label="cURL"
		language="bash"
		code={`curl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/webhook_endpoints" \\
  -H "Authorization: Bearer $SYLPHX_API_KEY" \\
  -H "Content-Type: application/json" \\
  -d '{"spec": {"uri": "https://example.com/webhooks", "event_types": ["*"]}}'`}
	/>
	<CodeTab
		label="TypeScript"
		language="ts"
		code={`import { Sylphx } from '@sylphx/sdk'

const sylphx = new Sylphx()
const { env } = await sylphx.access.whoami({})
const endpoint = await sylphx.events.webhookEndpoints.create({
  parent: env,
  webhookEndpoint: { spec: { uri: 'https://example.com/webhooks' } },
})
// endpoint.status.signingSecret is shown once: store it as WEBHOOK_SECRET.`}
	/>
</CodeTabs>

The answer carries `status.signing_secret` (it starts with `whsec_`) once.
Store it where your receiver reads it; [rotating it](/docs/api/webhook_endpoints#rotate-secret)
is the only way to get another.

The endpoint needs the `events:write` scope to create and the `events:read`
scope to read.

## Read what happened

A [delivery](/docs/api/webhook_deliveries) records every attempt and what your
endpoint answered. Read one to see why a delivery failed, and
[replay](/docs/api/webhook_endpoints#replay) the dead-lettered ones when your
receiver is back.

<RelatedDocs
	links={[
		{
			href: '/docs/webhooks/receiving',
			label: 'Receiving webhooks',
			description: 'Verify the signature, answer quickly and handle duplicates.',
		},
		{
			href: '/docs/webhooks/event-types',
			label: 'Event types',
			description: 'The events you can receive and the envelope they arrive in.',
		},
		{
			href: '/docs/webhooks/testing',
			label: 'Testing',
			description: 'Send a test event and read the delivery back.',
		},
		{
			href: '/docs/api/webhook_endpoints',
			label: 'The webhook_endpoints collection',
			description: 'Every method, field and example.',
		},
	]}
/>
