---
title: "Register a webhook endpoint"
method: POST
path: "/v1/webhook-endpoints"
tags: ["Webhooks"]
---

# Register a webhook endpoint

`POST /v1/webhook-endpoints`

## Request

- Base URL: `https://api.letmepost.dev`
- URL: `https://api.letmepost.dev/v1/webhook-endpoints`
- Auth: HTTP bearer

Authenticated with an API key or dashboard session. The `signingSecret` is returned **once** at creation and is used to verify the HMAC-SHA256 `X-Letmepost-Signature` header on every delivery. An empty `events` array subscribes to every event type.

## Headers

- `Idempotency-Key` string

## Request body

- CreateWebhookEndpointRequest
  - `url` string, uri, required
  - `events` WebhookEventType[]: Empty array subscribes the endpoint to every event type.
  - `description` string

## Response `201`

Endpoint created. `signingSecret` shown once.

- WebhookEndpointWithSecret
  - `id` string, required
  - `url` string, uri, required
  - `events` WebhookEventType[], required
  - `description` string, nullable
  - `active` boolean, required
  - `lastDeliveryAt` string, date-time, nullable
  - `lastFailureReason` string, nullable
  - `createdAt` string, date-time, required
  - `updatedAt` string, date-time, required
  - `signingSecret` string, required: HMAC-SHA256 signing secret — returned **once** at creation. Used to verify every delivery's `X-Letmepost-Signature` header.

## Other responses

- `400`: `validation_failed` — request body or query parameters didn't match the schema. Rules surfaced include `body.json`, `targets.required`, `targets.max`, `mode_conflict`, `targets.options.platform_mismatch`, `targets.account.platform_mismatch`, `target.account.not_connected`, `target.account.ambiguous`, `scheduledAt.future`, `scheduledAt.no_first_comment`, `scheduledAt.no_bluesky_reply`.
- `401`: `unauthenticated` — missing or invalid Bearer key.
- `409`: `idempotency_conflict` — the same `Idempotency-Key` was used with a different request body. Use a new key, or retry with the original body.
- `429`: `rate_limited` — too many requests. Inspect `X-RateLimit-Limit` (static ceiling) and the IETF-draft `RateLimit-*` headers (sliding-window remaining/reset) before retrying.
- `500`: `internal_error` — something on our end failed unexpectedly. Quote the `requestId` when reporting.

---

[API](https://skmtc.dev/letmepost/apis/letmepost-dev-api.md) · [All operations](https://skmtc.dev/letmepost/apis/letmepost-dev-api/llms.txt) · [OpenAPI document](https://skmtc.dev/letmepost/apis/letmepost-dev-api/revisions/f6659001f96b?raw)
