> ## Documentation Index
> Fetch the complete documentation index at: https://docs.so-me.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Webhooks

> Create embedded webhook subscriptions and verify signed event payloads

Embedded webhooks notify your server when activity occurs for any end user in
your partner project. Each payload identifies the affected end user so you can
route it back to the correct customer.

## Create a subscription

Webhook endpoints must use HTTPS and must be publicly reachable.

```ts theme={null}
const webhook = await soMe.webhooks.create({
  url: "https://app.example.com/webhooks/so-me",
  events: ["post.published", "post.failed"],
});

// Save this value immediately; it is returned only on creation.
await secrets.store("some-webhook-secret", webhook.secret!);
```

Omit `events`, or pass an empty array, to receive every recognized event that
occurs for your embedded end users.

```ts theme={null}
const subscriptions = await soMe.webhooks.list();
await soMe.webhooks.remove(webhook.id);
```

Useful events for an embedded publishing integration include:

| Event                         | Meaning                               |
| ----------------------------- | ------------------------------------- |
| `social.account_connected`    | A social account was connected        |
| `social.account_disconnected` | A social account was disconnected     |
| `post.scheduled`              | A post entered the publishing queue   |
| `post.published`              | The social platform accepted the post |
| `post.failed`                 | Publishing failed                     |

## Verify the signature

Social media studio signs the exact raw HTTP request body with HMAC-SHA256. Read the body as
text or bytes and verify it before parsing JSON.

```ts theme={null}
import {
  verifyWebhookSignature,
  type EmbeddedWebhookEvent,
} from "@social-media-scheduler/sdk";

export async function POST(request: Request) {
  const rawBody = await request.text();
  const signature = request.headers.get("x-webhook-signature") ?? "";

  if (
    !verifyWebhookSignature(
      rawBody,
      signature,
      process.env.SOME_WEBHOOK_SECRET!,
    )
  ) {
    return new Response("Invalid signature", { status: 401 });
  }

  const event = JSON.parse(rawBody) as EmbeddedWebhookEvent;
  await handleEvent(event);
  return new Response(null, { status: 204 });
}
```

The signature may be sent as a hexadecimal digest or with a `v1=` prefix; the
SDK accepts both forms.

## Payload shape

```json theme={null}
{
  "id": "delivery_uuid",
  "event": "post.published",
  "createdAt": "2026-08-20T10:00:04.000Z",
  "endUser": {
    "id": "end_user_uuid",
    "externalId": "customer_123"
  },
  "data": {
    "postId": "post_uuid",
    "platform": "THREADS"
  }
}
```

Delivery requests also include:

| Header                  | Description                           |
| ----------------------- | ------------------------------------- |
| `X-Webhook-Signature`   | HMAC-SHA256 signature of the raw body |
| `X-Webhook-Event`       | Event name                            |
| `X-Webhook-Delivery-Id` | Stable delivery identifier            |

## Delivery behavior

* Return a `2xx` response as soon as the event is durably accepted.
* Process events asynchronously when work may take more than a few seconds.
* Deduplicate deliveries using the payload `id` or
  `X-Webhook-Delivery-Id`.
* Non-`2xx` responses, network failures, and timeouts are retried with
  exponential backoff.
* Repeatedly failing subscriptions are automatically disabled.

Webhook delivery is at least once, so handlers must be idempotent.
