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

# Manage endpoints

> Every setting an endpoint has, what each one does and when to change it: subscriptions, payload style, ordering, rate limits, custom headers, pausing and deleting.

An **endpoint** is a URL that receives events, together with the settings that decide which events it gets and how. You manage endpoints with the Webhooks API. Changes to how an endpoint is delivered to, such as its URL, headers, rate limit, pause or a new signing secret, apply from the next delivery attempt; after a rotation the previous secret keeps signing alongside the new one for 24 hours. Changes to which events it receives, such as creating it or changing its event types or organisations, reach event matching within 15 seconds; an event published in that window follows the previous settings. To check a new endpoint straight away, send a test event, which is delivered directly.

## Endpoint settings

| Setting | What it does | Default |
| - | - | - |
| `url` | Where deliveries are sent. Must be `https://`, on the standard HTTPS port (443), with a certificate from a public certificate authority, and resolve to a public address. | Required |
| `description` | A note for your own team, up to 250 characters. | None |
| `eventTypes` | Which events it receives. See [Subscriptions](#subscriptions). | Required |
| `payloadStyle` | `Full` sends the whole record; `Minimal` sends only a reference to fetch. See [Full and Minimal payloads](/api-reference/webhooks/event-types#full-and-minimal-payloads). | `Full` |
| `ordering` | `Unordered` sends each event as soon as it happens. `Fifo` ("first in, first out") sends them one at a time, in the order they happened. See [Ordering](/api-reference/webhooks/delivery-and-retries#ordering). Set when the endpoint is created. | `Unordered` |
| `rateLimitPerSecond` | The most deliveries per second Flow Payroll sends to it, from 1 to 1,000. | No fixed limit: Flow Payroll adapts to how fast your receiver answers |
| `customHeaders` | Extra HTTP headers sent with every delivery. See [Custom headers](#custom-headers). | None |
| `alertContacts` | Up to 10 email addresses told when this endpoint keeps failing, instead of any defaults. See [Alert emails](#alert-emails). | None: the organisation's or account's defaults apply |
| `scope` | Read-only. `Organisation` for one organisation, or `Customer` for every organisation on your account, with `ownerId` naming which. Chosen with `?scope=customer` when the endpoint is created, and fixed after that. See [Account-wide endpoints](/api-reference/webhooks/account-wide-endpoints). | `Organisation` |
| `status` | `Active`, or `Paused` to stop deliveries for a while. Flow Payroll sets `DisabledBySystem` itself when an endpoint keeps failing. | `Active` |

An organisation can have up to 20 endpoints, and so can an account. That is plenty for most integrations: one endpoint per receiving system is the usual pattern.

Send every setting that has named values, such as `payloadStyle`, `ordering` and `status`, by name. A number in their place is refused.

## Create, read, change and delete

| To | Call |
| - | - |
| Create an endpoint | `POST /endpoints` |
| List your endpoints | `GET /endpoints` (add `scope=customer` for account-wide ones) |
| See one | `GET /endpoints/{endpointId}` |
| Change settings | `PATCH /endpoints/{endpointId}` with only the fields you are changing. See [Change settings](#change-settings) |
| Delete | `DELETE /endpoints/{endpointId}`, which answers with the endpoint as it was |

A `201` answer carries a `Location` header with the new endpoint's address.

Ids are prefixed so you can tell them apart: endpoints start `ep_`, events `evt_`, deliveries `dlv_`, signing secrets `sec_` and replays `rpl_`. The rest of an id is a sortable, time-based code, so ids created later sort after earlier ones.

## Change settings

`PATCH /endpoints/{endpointId}` takes a JSON Merge Patch ([RFC 7396](https://www.rfc-editor.org/rfc/rfc7396)):

* a field you leave out stays as it is;
* a field you send as `null` is removed;
* any other value replaces it.

```json theme={null}
{ "description": "Payroll to HR sync", "rateLimitPerSecond": null }
```

| Field | Sending `null` |
| - | - |
| `description` | Clears it |
| `rateLimitPerSecond` | Removes the cap |
| `customHeaders` | Removes every custom header |
| `alertContacts` | Removes the endpoint's own list, so the defaults apply again |
| `organisationIds` | Removes the filter, so the endpoint hears every organisation on the account again (account-wide endpoints) |
| `excludedOrganisationIds` | Excludes nobody (account-wide endpoints) |
| `url`, `status`, `payloadStyle`, `eventTypes` | Refused with `400` and `value_required`: these always have a value |

`eventTypes`, `organisationIds`, `excludedOrganisationIds` and `alertContacts` replace the whole list. They change the same lists as the endpoint's `event-types`, `organisations`, `excluded-organisations` and `alert-contacts` resources, which add and remove single entries. Use those to add or remove one entry without resending the rest.

Two changes to the same endpoint, or to the same default alert-contact list, at the same moment both apply. If the endpoint keeps changing under your request, you get a `409` with `concurrent_change`; send the request again.

## Signing secret and public key

Creating an endpoint answers with the endpoint and two values for checking its deliveries:

| Field | What it verifies | Can you read it again? |
| - | - | - |
| `publicKey` (`whpk_…`) | The `v1a` signature. Not a secret: it can check a signature but never make one. | Yes. Every signing secret in `GET /endpoints/{endpointId}/secrets` shows its own `publicKey`. |
| `signingSecret` (`whsec_…`) | The `v1` signature. Keep it secret: anyone holding it can sign. | No. It is shown only in this answer. |

You need only one of them, and the public key is recommended: your receiver then holds nothing that could sign a fake delivery. Each rotation issues a new secret and a new public key together. See [Verify signatures](/api-reference/webhooks/verify-signatures).

## Subscriptions

`eventTypes` holds up to 100 values of three kinds, which you can mix:

* an exact type, such as `payslip.finalised`;
* a family, such as `payslip.*`, meaning every event whose name starts `payslip.`, including ones added later;
* `*`, meaning every event.

Change them without resending the whole endpoint:

| To | Call |
| - | - |
| See them | `GET /endpoints/{endpointId}/event-types` |
| Replace the list | `PUT /endpoints/{endpointId}/event-types` with `{"eventTypes": [...]}` |
| Add some | `POST /endpoints/{endpointId}/event-types` with `{"eventTypes": [...]}` |
| Remove some | `DELETE /endpoints/{endpointId}/event-types?eventType=a&eventType=b` |
| Remove one | `DELETE /endpoints/{endpointId}/event-types/{eventType}` |

A remove answers with what it removed. Removing a type the endpoint does not have is a `404`, so a typo does not look like success. An endpoint always keeps at least one type: to stop all deliveries, [pause it](#pause-and-resume) or delete it.

## Custom headers

If your receiver sits behind its own gateway that expects, say, an `Authorization` or `X-Api-Key` header, add it as a custom header and Flow Payroll sends it with every delivery:

```json theme={null}
{ "customHeaders": { "X-Api-Key": "your-gateway-key" } }
```

* Up to 10 headers, each value up to 1,024 characters.
* Values are stored encrypted and never shown again. The endpoint shows only their names, in `customHeaderNames`.
* `PATCH` with `customHeaders` replaces them all; send `null` or `{}` to remove them.
* You cannot set `Host`, `Content-Length`, `Content-Type`, `Transfer-Encoding`, `X-Request-Id`, or any header starting `webhook-` or `X-Flow-`: Flow Payroll sets those itself.

Custom headers add to the signature check, never replace it. Always [verify the signature](/api-reference/webhooks/verify-signatures).

## Alert emails

When an endpoint has been failing for a day, and again if Flow Payroll disables it, we email the people who can fix it. You choose who that is, once for all your endpoints or per endpoint. For each email we use the first of these that has any addresses:

1. **The endpoint's own alert contacts**, `alertContacts` on the endpoint.
2. **The organisation's default alert contacts**, for an endpoint that belongs to one organisation.
3. **The account's default alert contacts**, shared by every organisation on the account.
4. **Otherwise**, whoever created the endpoint and the account owner. An endpoint created with an API token has no person behind it, so only the account owner is told.

Only one level is used: an endpoint with its own list does not also email the defaults.

The email names the endpoint (its id, URL and description), says when it started failing and gives the last status code and error. It contains no event data.

<Tip>
  A bureau running payroll for 500 organisations sets its support inbox once, as the account's defaults, and every endpoint on every organisation uses it, including endpoints and organisations added later. If one client wants its own finance team told instead, set that organisation's defaults, or that one endpoint's list.
</Tip>

### Who is actually emailed

Every alert-contacts response carries the list set at that level (`emails`, empty when it is not set) and who is actually emailed (`effective`):

```json theme={null}
{
  "emails": [],
  "effective": { "emails": ["payroll-support@example.com"], "source": "Account" }
}
```

| `effective.source` | Emails come from |
| - | - |
| `Endpoint` | The endpoint's own alert contacts |
| `Organisation` | The organisation's default alert contacts |
| `Account` | The account's default alert contacts |
| `Default` | Nobody set any: the endpoint's creator, when a person created it, and the account owner |

A level is either set or not set. Removing its last address, or replacing its list with `[]`, clears it, and it inherits from the next level again.

### Defaults for an organisation or the account

| To | Call |
| - | - |
| See them | `GET /alert-contacts` |
| Replace the list | `PUT /alert-contacts` with `{"emails": [...]}` |
| Add some | `POST /alert-contacts` with `{"emails": [...]}` |
| Remove some | `DELETE /alert-contacts?email=a@example.com&email=b@example.com` |
| Remove one | `DELETE /alert-contacts/{email}`, with the address URL-encoded (`ops%40example.com`) |

These act on the organisation you are signed in to. Add `scope=customer` to act on the account's defaults instead, which needs account-wide access, as [account-wide endpoints](/api-reference/webhooks/account-wide-endpoints) do. A change applies to every endpoint straight away; allow a minute before the next email uses it.

### One endpoint's list

| To | Call |
| - | - |
| See them | `GET /endpoints/{endpointId}/alert-contacts` |
| Replace the list | `PUT /endpoints/{endpointId}/alert-contacts` with `{"emails": [...]}` |
| Add some | `POST /endpoints/{endpointId}/alert-contacts` with `{"emails": [...]}` |
| Remove some | `DELETE /endpoints/{endpointId}/alert-contacts?email=a@example.com&email=b@example.com` |
| Remove one | `DELETE /endpoints/{endpointId}/alert-contacts/{email}` |

You can also set `alertContacts` when you create the endpoint, or with `PATCH /endpoints/{endpointId}`.

### Rules for every list

* Up to 10 addresses. Each must be a single plain address, such as `ops@example.com`: no names, and no lists in one entry.
* Addresses are stored in lower case, and a duplicate is kept once.
* A remove answers with what it removed. Removing an address the list does not have is a `404`.
* Removing the last address, or sending an empty list, hands over to the next level.

See [When an endpoint keeps failing](/api-reference/webhooks/delivery-and-retries#when-an-endpoint-keeps-failing) for when the emails are sent.

## Pause and resume

`PATCH /endpoints/{endpointId}` with `{"status": "Paused"}` stops deliveries, for example while you deploy a new receiver. Events that happen meanwhile are recorded as `Skipped` rather than queued, so nothing floods in when you resume. Set `{"status": "Active"}` to resume, then [replay](/api-reference/webhooks/replay-and-recovery#replay-a-window) the window with `"statuses": ["Skipped"]` if you want what you missed.

## When Flow Payroll disables an endpoint

An endpoint that has failed every delivery for five days in a row, or that answered `410 Gone`, is set to `DisabledBySystem`, and `statusReason` says which (`failing_for_5_days` or `endpoint_gone`). New events are recorded as `Skipped`. To bring it back, fix the receiver and set `{"status": "Active"}`. See [When an endpoint keeps failing](/api-reference/webhooks/delivery-and-retries#when-an-endpoint-keeps-failing).

## Make requests safe to retry

Send an `Idempotency-Key` header, a unique value you choose such as a UUID, when you create an endpoint, rotate a secret, resend a delivery or start a replay. If the request times out and you send it again with the same key and the same body within 24 hours, you get the first answer back instead of a second endpoint. The same key sent by someone else, or with a different body, is a `409` with `idempotency_key_reused`.

## Scope values

Wherever a route takes `scope`, send `organisation` (the default) or `customer`. `GET /endpoints` also takes `all`. Any other value is a `400` with `invalid_scope`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.