# Add a webhook endpoint

`POST https://api.parasend.com/v1/webhook_endpoints`

Source: https://parasend.com/docs/api/webhooks/create

Authentication: `Authorization: Bearer <API key>` (a `ps_test_` key in test mode, `ps_live_` in live mode).
Send an `Idempotency-Key` header: a retry with the same key returns the first answer.

Adds an endpoint in the key's mode (test or live). The `url` must be HTTPS on port 443, without a user name or password, and its host a DNS name (not an IP address) whose every A and AAAA record is a public address. A host that does not resolve is refused, so a typo shows now rather than after three days of retries. `auth.oauth2.token_url` follows the same rules. The signing `secret` is in this response only: store it. The endpoint records who created it (`created_by`). Errors: `webhook_url_invalid`, `webhook_url_port_not_allowed`, `webhook_url_not_public`, `webhook_url_unresolvable`, `webhook_event_type_unknown`, `webhook_auth_config_invalid`, `webhook_delivery_sources_invalid`, `webhook_endpoint_limit_reached`.

## Body (application/json)

- `url` (string, required): HTTPS only.
- `event_types` (array of string, optional): One of: `delivery.created`, `delivery.rejected`, `delivery.notified`, `delivery.opened`, `delivery.email_opened`, `delivery.auth_failed`, `delivery.confirmed`, `delivery.downloaded`, `delivery.delivered`, `delivery.reminded`, `delivery.bounced`, `delivery.expired`, `delivery.revoked`, `delivery.consignment_failed`, `delivery.evidence_sealed`, `delivery.evidence_timestamp_deferred`, `delivery.evidence_timestamp_recovered`, `delivery.paper_pending_approval`, `delivery.paper_dispatched`, `delivery.paper_failed`, `document.previewed`, `batch.finalized`, `batch.completed`, `lookup.completed`, `consent.granted`, `consent.withdrawn`, `invitation.sent`, `link.opened`, `otp.sent`, `otp.verified`, `otp.locked`, `secret.attached`, `secret.available`, `secret.revealed`, `secret.copied`, `secret.expired`, `secret.exhausted`, `secret.revoked`, `request.sent`, `request.opened`, `request.fulfilled`, `request.expired`, `request.closed`, `request.reopened`, `deletion.scheduled`, `recipient_domain.verified`, `recipient_domain.degraded`, `recipient_domain.failed`, `member.suspended`, `member.restored`, `member.removed`, `batch.cancelled`, `batch.policy_changed`, `delivery.policy_changed`, `email_sender.verified`, `email_sender.degraded`, `email_sender.failed`, `email_sender.restored`.
- `description` (string · nullable, optional): Your own note about the endpoint, up to 500 characters; null or left out for none.
- `auth` (object, optional): How Parasend authenticates to your endpoint, in addition to the HMAC signature, which is always sent. Secrets are write-only: accepted on create and update, never returned. The endpoint shows `mode`, the settings that are not secret, and whether a secret is set.
  - `auth.mode` (string, required): none = HMAC only. static_bearer = an `Authorization` header carrying `Bearer <token>` with a customer-supplied token. oauth2_client_credentials = a token obtained from the customer's IdP by the client-credentials grant, cached until expiry, sent as the bearer. One of: `none`, `static_bearer`, `oauth2_client_credentials`. Default: `"none"`.
  - `auth.static_bearer` (object · nullable, optional): With `mode: static_bearer`: the `token` Parasend sends as `Authorization: Bearer <token>`, at most 4,096 characters (write-only; responses show `token_set`). null for any other mode.
    - `auth.static_bearer.token` (string, optional): Never returned. Rotate by writing a new value.
  - `auth.oauth2` (object · nullable, optional): With `mode: oauth2_client_credentials`: your token endpoint (`token_url`, HTTPS), `client_id`, `client_secret` (write-only; responses show `client_secret_set`), optional `scope` and `audience`, and `auth_style`. null for any other mode.
    - `auth.oauth2.token_url` (string · uri, optional): HTTPS only.
    - `auth.oauth2.client_id` (string, optional)
    - `auth.oauth2.client_secret` (string, optional): Never returned.
    - `auth.oauth2.scope` (string · nullable, optional)
    - `auth.oauth2.audience` (string · nullable, optional)
    - `auth.oauth2.auth_style` (string, optional): One of: `client_secret_basic`, `client_secret_post`. Default: `"client_secret_basic"`.
- `delivery_sources` (object · nullable, optional): Omitted or null = every delivery (the default; what every endpoint had before).

## Example: Add an endpoint for delivery events

```bash
curl -X POST "https://api.parasend.com/v1/webhook_endpoints" \
  -H "Authorization: Bearer $PARASEND_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.fyrirtaeki.example/parasend",
    "description": "Case system",
    "event_types": [
      "delivery.notified",
      "delivery.confirmed",
      "delivery.evidence_sealed"
    ]
  }'
```

```js
const res = await fetch('https://api.parasend.com/v1/webhook_endpoints', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.PARASEND_KEY}`,
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://hooks.fyrirtaeki.example/parasend',
    description: 'Case system',
    event_types: ['delivery.notified', 'delivery.confirmed', 'delivery.evidence_sealed'],
  }),
});
const data = await res.json();
```

## Response 201

Created (includes `secret`, shown only now).

```json
{
  "id": "whk_01M6R27T4KCSN7M0CHFGVH7JRA",
  "object": "webhook_endpoint",
  "url": "https://hooks.fyrirtaeki.example/parasend",
  "event_types": [
    "delivery.notified",
    "delivery.confirmed",
    "delivery.evidence_sealed"
  ],
  "description": "Case system",
  "secret": "whsec_ORQVKTHUQmNP3FrN6SNpPik8rEBsOLrSX63VnJQeaPM",
  "auth": {
    "mode": "none",
    "static_bearer": null,
    "oauth2": null
  },
  "delivery_sources": null,
  "status": "enabled",
  "livemode": false,
  "exhausted_events": 0,
  "last_exhausted_at": null,
  "retrying_events": 0,
  "last_attempt": null,
  "created_by": {
    "user_id": "usr_01M6R27SYD4B50414S0BZ4KBKR",
    "name": "Jóna Jónsdóttir",
    "via": "api_key"
  },
  "previous_secret_expires_at": null,
  "created": "2026-11-06T09:15:05.746Z"
}
```

Errors: one shape, `{"error": {type, code, message, param, request_id}}`; every code is listed at https://parasend.com/docs/errors.
