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

# FAQ

> Common questions about integrating with the Endl Partner API

Answers to the questions partners ask most often while integrating. Each heading
links directly, so you can point a colleague at a single answer.

## Getting started

### What do I need before my first call?

An API key issued by Endl, carrying the permissions for the endpoints you intend
to call. There is no sign-up endpoint and no login step — keys are issued to your
partner account by Endl.

### Is there a sandbox?

Yes.

| Environment | Base URL                      |
| ----------- | ----------------------------- |
| Sandbox     | `https://api-sandbox.endl.io` |
| Production  | Issued at onboarding          |

Every **Try it** button in this documentation sends to sandbox, so you can
exercise an endpoint from the page you are reading.

### Which API do I need?

| API                                                  | Base path          | Purpose                                                             |
| ---------------------------------------------------- | ------------------ | ------------------------------------------------------------------- |
| [Onboarding](/api-reference/onboarding/introduction) | `/api/v0/customer` | Verify end customers — KYC for individuals, KYB for businesses      |
| [Partner](/api-reference/introduction)               | `/api/v0/…`        | Wallets, quotes, recipients, accounts, transactions, reference data |
| [Webhooks](/webhooks/overview)                       | `/api/v0/webhooks` | Subscriptions, delivery inspection, and the PULL event feed         |

One key authenticates all three.

### How do I get production access?

Your production base URL and credentials are issued by Endl at onboarding. Point
your client at that host and use the credentials issued with it — the request and
response shapes are identical to sandbox.

## Authentication

### Do I need an API secret?

No. Authentication is a single header, `API-KEY`. The `API-SECRET` header was
removed and is no longer part of any endpoint.

```bash theme={null}
curl https://api-sandbox.endl.io/api/v0/currencies \
  -H "API-KEY: $ENDL_API_KEY"
```

### My key is valid, so why am I getting a `400`?

Almost always a missing permission. Endpoints are gated by `quotes`,
`recipients`, `accounts`, `webhooks`, `events` and `onboarding`, and a key
without the required one is rejected as a validation failure rather than an
authorisation failure.

<Warning>
  The two APIs differ here. On the **Partner and Webhooks APIs** a missing
  permission is `400 VALIDATION_ERROR`. On the **Onboarding API** it is `403`.
  Check the key's permissions before you start debugging the body.
</Warning>

### What is the difference between `401` and `503`?

`401` means your credentials were rejected — the header is missing, the key is
unknown, or the partner is inactive. These are deliberately indistinguishable,
and retrying will never succeed.

`503 DIRECTORY_UNAVAILABLE` means Endl could not reach the credential store. Your
key may be perfectly valid, so retry with backoff.

### Can I call the API from a browser or a mobile app?

No. The key authenticates your partner account, not an end user, so it belongs
server-side only. Never ship it in client code or commit it to source control.

### What do I do if a key leaks?

Ask Endl to issue a replacement credential.

## Requests and responses

### What shape are responses?

Two shapes, which is the one inconsistency worth holding in mind:

| API               | Shape                                                           |
| ----------------- | --------------------------------------------------------------- |
| Partner, Webhooks | The shared envelope — `{ code, message, status, data, errors }` |
| Onboarding        | **Flat** — the body *is* the data object                        |

Onboarding keeps two exceptions: `429` returns the older envelope and `503`
returns the `errors[]` form. Parse defensively — check for `errors` first, then
fall back to the flat fields.

### Should I trust the HTTP status or the body?

Both. On the Partner and Webhooks APIs a response is only successful when
`status` is `SUCCESS` **and** `errors` is empty. See
[Errors and the response envelope](/api-reference/errors).

### Why am I never getting a `429`?

The Partner and Webhooks APIs never return `429`. Rate limits surface as
`400 VALIDATION_ERROR` instead, so do not build retry logic around a `429`
response. The Onboarding API is the exception — it does return `429`.

### How should I handle money values?

As decimal strings, never floats — `"1500.00"`. Parse them with a decimal type.
Binary floats lose cents at scale.

### How does pagination work?

Opaque, forward-only cursors. Call with no `after`, read `data.nextCursor`, and
call again with `after=<nextCursor>` and the **same** filters until the cursor is
`null`. `limit` defaults to `25` and caps at `100`.

<Warning>
  **A short page is not the end.** Filters are applied after the query, so a page
  can return fewer items than `limit` — even zero — while `nextCursor` is still
  non-null. Follow the cursor until it is `null`.
</Warning>

### Which failures are safe to retry?

| Codes                                    | Retry?                                 |
| ---------------------------------------- | -------------------------------------- |
| `400`, `401`, `404`, `405`, `409`, `422` | No. Fix the request or the credential. |
| `500`, `503`                             | Yes, with backoff.                     |

<Warning>
  Never blindly retry a write that may have succeeded —
  [Submit transaction](/api-reference/transactions/submit-transaction) in
  particular. Read the transaction back before resubmitting it.
</Warning>

### Why did I get a `404` for a resource I know exists?

Identity is derived from your API key, and you only ever see your own resources.
Another partner's resource returns `404`, never `403`, so the API never reveals
that it exists.

## Onboarding

### What is the difference between KYC and KYB?

KYC verifies an individual; KYB verifies a business and its beneficial owners.
Step-by-step walkthroughs:
[Individual (KYC)](/api-reference/onboarding/individual-kyc) and
[Business (KYB)](/api-reference/onboarding/business-kyb).

### What file types and sizes are accepted for documents?

JPEG, PNG or PDF, at most **2 MB** per file. Content is sniffed rather than
trusted from the extension. A larger file returns `413`.

### What do the customer statuses mean?

| Status      | Meaning                                       |
| ----------- | --------------------------------------------- |
| `INITIATED` | Created; documents not yet submitted          |
| `PENDING`   | Documents submitted; verification in progress |
| `COMPLETED` | Approved                                      |
| `REJECTED`  | Rejected                                      |

### How do I know when verification finishes?

Subscribe to the `kyc.completed` and `kyc.rejected`
[webhook events](/webhooks/reference), or poll
[Get customer](/api-reference/onboarding/get-customer).

### Why did creating a customer return `403`?

Three different causes share that code — the `message` field distinguishes them:

* Your key lacks the `onboarding` permission
* The customer belongs to another partner
* [The country is restricted](/api-reference/onboarding/country-restrictions),
  which reads `Onboarding is not permitted for this country`

The country check runs **before anything is created**, so nothing needs cleaning
up afterwards.

### A create timed out with `503`. Can I retry it?

Yes, with the same body. Endl reconciles rather than creating a duplicate.

## Payouts

### What is the sequence of calls?

1. Ask [which fields the destination currency requires](/api-reference/reference/get-recipient-required-fields),
   then [add the recipient](/api-reference/recipients/add-recipient)
2. [Generate a quote](/api-reference/quotes/generate-quote)
3. [Create a pre-transaction](/api-reference/transactions/create-pre-transaction)
   to lock the quote in and receive deposit instructions
4. [Submit](/api-reference/transactions/submit-transaction), then poll
   [Get transaction](/api-reference/transactions/get-transaction) or subscribe to
   `payout.completed`

### Why was my recipient rejected as invalid?

Required fields vary by destination currency and rail and are not guessable —
a GBP domestic payout needs different fields from a SEPA or SWIFT one. Call
[Get recipient required fields](/api-reference/reference/get-recipient-required-fields)
first and collect exactly what it returns.

### Which currencies, countries and rails are supported?

These catalogues change, so they are served by the API rather than listed here:

| Question                         | Endpoint                                                                                |
| -------------------------------- | --------------------------------------------------------------------------------------- |
| Which currencies?                | [List currencies](/api-reference/reference/list-currencies)                             |
| Which countries?                 | [List countries](/api-reference/reference/list-countries)                               |
| Which payment rails?             | [List rails](/api-reference/reference/list-rails)                                       |
| Which fields for this recipient? | [Get recipient required fields](/api-reference/reference/get-recipient-required-fields) |

Cache the results rather than re-fetching them on every request.

## Webhooks

### Should I use PUSH or PULL?

| Mode   | How it works                                                                                     | You host   |
| ------ | ------------------------------------------------------------------------------------------------ | ---------- |
| `PUSH` | Endl sends each event to your HTTPS endpoint as a signed `POST`, with retries. The default.      | A receiver |
| `PULL` | Endl delivers nothing. You poll the [event feed](/webhooks/pull-events) and checkpoint a cursor. | Nothing    |

`SQS` and `SSE` are reserved but not yet available — requesting either returns
`422`.

### How do I verify a delivery came from Endl?

HMAC-SHA512 over `timestamp + "." + body`, compared against
`X-WEBHOOK-SIGNATURE`. Three things to get right: it is SHA-**512**, the hex is
**lowercase**, and you must hash the **raw received bytes** — parsing and
re-serializing the JSON changes those bytes and breaks the signature. Worked
Python and Node examples are on
[Verifying signatures](/webhooks/signature-verification).

### I lost my signing secret. Can I retrieve it?

No. It is returned **once**, when the subscription is created. The only way to
get a working secret again is to [rotate it](/webhooks/rotate-secret).

### What happens if my endpoint is down?

Endl retries, and every attempt is recorded. A delivery ends as `DELIVERED`,
`FAILED`, `EXPIRED` or `CANCELLED`; only `FAILED` and `EXPIRED` can be
[retried](/webhooks/retry-delivery). Inspect what happened with
[List deliveries](/webhooks/list-deliveries), which includes the response your
endpoint returned.

### Why is my subscription `SUSPENDED`?

Endl suspends a subscription after repeated delivery failures. You cannot set
that status yourself. `PAUSED`, which you control, holds events rather than
dropping them — they drain when you resume.

### How many subscriptions can I have?

Five active per partner by default. `target` is limited to 512 characters.

### Will you send event types I do not recognise?

Yes. Endl manages the catalogue and adds types over time, so handle an
unrecognised `eventType` gracefully rather than rejecting it — otherwise a new
event type becomes an outage in your receiver. Subscribing with a glob such as
`payout.*` picks up related types automatically.

### Are PULL feed responses signed?

No. You are reading from Endl over an authenticated connection, so there is
nothing to verify. Signatures exist because PUSH arrives unsolicited.

### Why does a delivery use an idempotency key?

Retries mean your endpoint can legitimately receive the same event more than
once. Use `X-WEBHOOK-EVENT-ID` to deduplicate.

## Still stuck?

Deliveries and errors are inspectable from the API itself — start with
[List deliveries](/webhooks/list-deliveries) for webhook problems and
[Errors and the response envelope](/api-reference/errors) for everything else.
If that does not resolve it, contact your Endl representative with the failing
request, the response body, and the timestamp.
