> ## 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.

# Integrations API overview

> Connect Flow Payroll to a pension provider, link a scheme to the connection, follow every submission and every read from the provider, and keep the provider's data fresh. NEST is the first provider.

The Integrations API is how Flow Payroll talks to third-party providers on your behalf. Today that means **NEST**: you connect an employer's NEST account once, link a pension scheme to it, and the Pensions API can then send enrolments and contribution schedules straight to NEST, approve the payment, and read back what NEST reports. Everything sent or read is recorded as an operation you can follow, retry or cancel.

The service lives at `/v1/integrations` relative to the gateway base URLs on the [API reference introduction](/api-reference/introduction), for example `https://api.sandbox.flowpayroll.ai/v1/integrations` in sandbox.

<Note>
  The Integrations API holds the connection and the record of what happened. The submissions themselves are made through the [Pensions API](/api-reference/pensions-v2/overview#provider-submissions), which knows what a pay run contains. Read this page for how connections, freshness and reliability work; read that page for how to send a pay run.
</Note>

## Key concepts

* **Provider**: an entry in the catalogue of services Flow Payroll can send to. `GET /providers` tells you how each one is signed in to, which sign-in details it needs, what it can do and which kinds of data it can be read for. NEST's slug is `nest`.
* **Connection**: one employer's account with a provider. It holds the sign-in details (stored encrypted, never returned), the employer reference the provider knows the employer by, and the connection's health. Its `state` is `PendingAuth` until sign-in details are saved, `Callable` once a test has succeeded, `Invalid` if the provider has since rejected them, and `Deauthorised` after a disconnect.
* **Scheme link** (a *binding*): the record that a pension scheme sends through a connection. Linking is the opt-in. A scheme that is not linked keeps delivering by export file, and nothing is ever sent for it.
* **Operation**: one thing sent to a provider or read from it: a batch of enrolments, a contribution schedule, a payment approval, or a read of opt-outs, stopped contributions, refunds, schedules or setup data. Every operation has a status, a count of attempts, and the provider's own reference once it has one.
* **Provider data** (an *import*): data Flow Payroll reads from the provider rather than sends to it, one kind at a time: `pension.setup` (groups and payment sources), `pension.schedules`, `pension.opt-outs`, `pension.stopped-contributions` and `pension.refunds`. Each kind records when it was last read and the date it is synced through.
* **Initial import** (a *sync session*): a one-off read of everything the provider already holds for the employer, so an employer moving to Flow Payroll starts from the provider.
* **Message token**: every failure carries a stable, provider-agnostic `token` plus default English text. Branch on the token; show the text. The vocabulary is listed [below](#message-tokens).

## What happens when something is sent

1. A consumer, normally the Pensions API acting on your instruction, asks for a submission. An operation is created with status `Pending` and the request is acknowledged immediately.
2. The submission is sent to the provider. NEST processes submissions asynchronously: it acknowledges the request, then finishes it minutes or hours later. While that happens the operation is `AwaitingProvider`, and `expectedReadyDate` says when the provider expects to be done.
3. Flow Payroll collects the result as soon as the provider has it. The operation becomes `Succeeded`, and for a contribution schedule `providerReference` carries the reference the provider assigned, which is what a later payment approval refers to.
4. If the provider rejects the submission, or part of it, the operation becomes `NeedsAttention` with a token saying why, and the provider's detailed response is kept for support to inspect.
5. The consumer is told the outcome, so the Pensions API's own record of the pay run is updated without you polling this service.

`GET /operations` lists everything in this cycle, newest first, and `GET /operations/{operationId}` follows one item.

### Operation statuses

| Status | Meaning |
| - | - |
| `Pending` | Queued, not yet sent, or waiting for its next attempt (`nextAttemptDate`). Can be cancelled. |
| `AwaitingProvider` | Accepted by the provider, which is still processing it. `expectedReadyDate` says when to expect a result. |
| `Succeeded` | The provider accepted it in full. |
| `Failed` | The last attempt failed and no automatic retry is due. Can be retried. |
| `NeedsAttention` | Stopped for a reason a retry alone will not fix: the provider rejected it, rejected part of it, rejected the sign-in details, or has not answered in two days. `lastErrorToken` says which. Can be retried once the cause is dealt with. |
| `Cancelled` | Cancelled before it reached the provider. |

## Reliability

The guarantees below are the same for every provider and every kind of operation.

* **Nothing is lost.** An operation that cannot be completed is parked as `NeedsAttention`, never discarded. It stays in the activity list with its reason until it is retried or cancelled.
* **Temporary failures retry themselves.** If the provider cannot be reached or answers with a temporary error, the operation is retried automatically: up to five attempts spread over roughly eight hours, with the gaps growing from about half a minute to six hours. If the provider says how long to wait, that is honoured instead. `attempts` and `nextAttemptDate` show where an operation is in that cycle. Only when the last attempt fails does it park.
* **Failures a retry cannot fix park at once.** The provider rejecting the sign-in details (`provider_authentication_failed`), locking the account (`provider_account_locked`) or rejecting the content of a submission (`provider_validation_failed`) are not retried, because repeating them cannot help and repeated sign-in failures can lock the account on the provider's side. They park immediately with the reason, and you retry them once the cause is fixed.
* **Partial acceptance is never reported as success.** If the provider accepts some members and rejects others, the operation is `NeedsAttention` with `provider_partial_failure`.
* **Repeated failures pause the connection, briefly.** After five failures in a row on one connection, sending through it pauses for ten minutes (`connection_circuit_open`) and resumes on its own. If the provider is failing for everyone, sending pauses for all connections to it until it recovers (`provider_circuit_open`). Paused work is not lost; it waits.
* **A provider that goes quiet is flagged.** If the provider acknowledged a submission but has not returned a result within two days, the operation parks as `provider_result_overdue`, for you to check with the provider and then retry or cancel.
* **Sending twice is safe.** Every submission carries an idempotency key. Sending the same request again returns the original operation rather than creating a second one, so a retried call or a duplicated message cannot submit twice. The Pensions API derives its key from the pay run and the content it sends, so an unchanged run is not re-sent and a corrected one is.
* **Calls are paced.** Requests to a provider are kept within a conservative budget per connection (for NEST, 30 a minute and 2,000 a day, published on the provider entry). If the provider asks Flow Payroll to slow down (`provider_rate_limited`), the operation waits and continues automatically.
* **We are alerted too.** A parked operation, a paused connection and a provider that stops answering all raise alerts on our side, so support sees them without being told.

## Keeping provider data fresh

Some of what decides a pay run lives with the provider: a member who opts out or stops contributions tells NEST, not Flow Payroll. Flow Payroll therefore reads that data from the provider and applies it, on three triggers:

* **Ahead of a pay run.** The Pensions API asks for opt-outs and stopped contributions to be no older than six hours when a pay run is opened, and reads them again at the moment the run is locked, so the deductions are decided on what the provider knows at that moment.
* **On a schedule.** Opt-outs and stopped contributions are read at least daily. Setup data, schedules and refunds are read at least weekly, and again after every successful submission, because a submission is what changes them.
* **On demand.** `POST /connections/{connectionId}/imports` asks for one or more kinds to be no older than `maxStalenessMinutes` (`0` reads now). The response says, per kind, whether the data was already `Fresh`, a read was already `InFlight` (you are joined to it, not duplicated), a new read was `Requested`, or the kind is `Unavailable` for that provider.

`GET /connections/{connectionId}/imports` shows the state per kind: `lastSuccessDate`, the date the data is `syncedThroughDate`, whether a read is in flight, `nextDueDate`, and `consecutiveFailures` with `lastErrorToken` if reads are failing. Each read starts from a few days before the last successful one, so a change reported by the provider between two reads is never missed, and the synced-through date only moves forward when a read succeeds.

**Reading never changes anything by itself.** What is read is handed to the consumer that asked for it, and the consumer decides. For pensions, a reported opt-out is queued on `GET /provider-sync/member-changes` until somebody applies it, because it changes what a member is paid and carries a refund duty; applying it is what ends their Flow Payroll membership, on the date the provider gives. The Pensions API's [reconciliation endpoint](/api-reference/pensions-v2/overview#provider-submissions) shows how many were reported, applied and unmatched.

## Message tokens

Every failure and every "cannot do that yet" carries a token and default English. The token is stable across releases; the text may be reworded. Treat an unrecognised token as a generic failure, because the list grows.

| Token | Default text |
| - | - |
| `provider_rate_limited` | The provider asked us to slow down. Sending carries on automatically in a few minutes. |
| `provider_authentication_failed` | The provider rejected the sign-in details. Re-enter them on the connection, then try again. |
| `provider_validation_failed` | The provider rejected the submission because some of the details did not pass its checks. |
| `provider_unavailable` | The provider could not be reached. It is retried automatically. |
| `provider_account_locked` | The provider has locked this account. Sign in to the provider directly to unlock it, then test the connection. |
| `provider_partial_failure` | The provider accepted some of the workers and rejected the rest. |
| `provider_capability_unsupported` | The connected provider does not support this kind of submission. |
| `provider_circuit_open` | The provider is not responding for anyone right now. Sending is paused and starts again automatically once it recovers. |
| `provider_result_overdue` | The provider accepted this but has not returned a result in two days. Check it with the provider, then retry or cancel. |
| `provider_call_failed` | The provider did not complete the request. |
| `connection_circuit_open` | Sending is paused after repeated failures from the provider, and starts again automatically in about ten minutes. |
| `connection_not_callable` | This connection is not ready to use. Finish connecting it, then test it, before sending. |
| `connection_has_bindings` | The connection is still linked to a scheme. Unlink it before disconnecting or removing it. |
| `binding_not_found` | The record is not linked to any connection. |
| `operation_not_retryable` | The operation is still running or has already finished. |
| `operation_not_cancellable` | The operation has already reached the provider. |

## Typical workflow

1. Read the catalogue with `GET /providers` and note the provider's `requiredCredentialFields`. For NEST they are `username`, `password` and `employerReference`.
2. `POST /connections` with `providerSlug: "nest"`, then `PUT /connections/{connectionId}/credentials` with the employer's NEST sign-in (their own or a delegate's) and their employer reference, the `EMP` number.
3. `POST /connections/{connectionId}/test`. On success the connection is `Callable` and NEST's groups and payment sources are read in the background; `GET /connections/{connectionId}/pension/setup` returns them once `status` is `ready`. Set the scheme's payment source to one of the names returned.
4. Link the scheme: `PUT /bindings/pension/pension-scheme/{schemeId}` with the `connectionId`. From now on the scheme can be sent.
5. If the employer already uses NEST, `POST /connections/{connectionId}/sync-sessions` reads what NEST holds, and `GET /connections/{connectionId}/sync-sessions/latest` shows progress per kind.
6. Complete a pay run, then send it through the [Pensions API](/api-reference/pensions-v2/overview#provider-submissions). Follow the resulting operations here with `GET /operations?connectionId=...`, and retry or cancel from here if needed.

## Notes and best practices

* **Nothing sends on its own.** Completing a pay run produces the export files and stops. A submission only happens when the Pensions API is asked for one, and payment approval is a separate, explicit call.
* **The export files never go away.** A linked scheme still produces its provider files on every pay run, so the manual route stays available as a fallback.
* **Sign-in details are write-only.** `PUT .../credentials` stores them encrypted; no endpoint returns them. Test after every change.
* **Pause rather than disconnect** when you want a temporary stop: `PUT /connections/{connectionId}` with `enabled: false` keeps the sign-in and the history. `POST .../disconnect` removes the stored sign-in and keeps the history; `DELETE` removes the connection for good, and both refuse while a scheme is still linked (`connection_has_bindings`).
* **Follow the pagination token** on `GET /operations`: keep going while `nextPaginationToken` is non-null, even when a page comes back empty.
* **Key your handling off tokens**, never off message text, and expect new tokens to appear.

## Authentication

Every request needs an `X-Auth-Token` header, and multi-organisation credentials also send `X-Org-Id`; see [Authentication](/api-reference/authentication). Responses use the shared envelope described in [Response format](/api-reference/response-format).


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