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

# Getting Started

> What you need to know and have in place before you can start developing against the Ebury API.

The Ebury API has been designed for ease of use, but there are a number of things that need to happen or you need to know before you can start developing against it.

## Onboarding

In order to use the API your company needs to be one of the following:

* To be an existing Ebury customer with access to Ebury Online;
* To be on-boarded as an Ebury customer with access to Ebury Online: Currently this is an out-of-band process that needs to be completed by the Ebury sales team.

<Note>
  Live on-boarding functionality is planned for a future phase of the API.
</Note>

## Credentials

With an active Ebury Online account you need a few details to call the API:

* A client secret that is used in the Authentication workflow described [below](/api/authentication);
* An access token which you will get by implementing the Authentication workflow;
* A client account identifier that links a user to a given account and needs to be passed to the Authentication workflow;
* You'll need to supply a redirect URL, which will be used during the Authentication workflow.

## Environments

The following is a list of environments available when developing against or using our API:

**Environment Endpoints**

| URL | Purpose |
| - | - |
| `https://sandbox.ebury.io` | Sandbox API endpoint |
| `https://auth-sandbox.ebury.io` | Sandbox authentication endpoint |
| `https://api.ebury.io` | Production API endpoint |
| `https://auth.ebury.io` | Production  authentication endpoint |
| `https://trustedsandbox.ebury.io` | [mTLS](/api/authentication#trusted-authentication) Sandbox (both authentication and API) |
| `https://trusted.ebury.io` | [mTLS](/api/authentication#trusted-authentication) Production (both authentication and API) |

## API Description

Whilst each subject area is documented below they are also supported by an individual Swagger specification document. Please use the links below to download for your desired environment:

| Environment | JSON | YAML |
| - | - | - |
| `Sandbox` | [https://sandbox.ebury.io/openapi.json](https://sandbox.ebury.io/openapi.json) | [https://sandbox.ebury.io/openapi.yaml](https://sandbox.ebury.io/openapi.yaml) |
| `Production` | [https://api.ebury.io/openapi.json](https://api.ebury.io/openapi.json) | [https://api.ebury.io/openapi.yaml](https://api.ebury.io/openapi.yaml) |

## Rate limiting

Rate limiting of the Ebury API is primarily on a per-client basis. If you receive a response with a status code of `429 Too Many Requests`, it means that you have been rate limited for sending too many requests, and should wait before sending further requests.

## Error Handling

The Ebury API tries to honour HTTP return codes relevant to the error that is being conveyed. However, 4xx HTTP return codes are also used as a "blanket" with more information to be found in the response body of the [error message](#error-message) e.g., a `409` will indicate an issue with the data sent that can be rectified: you should consult this message to help you take corrective action.

## Idempotency

The Ebury API is not exposing idempotent operations by default.

The `POST` requests can however include a header `X-Idempotency-Id`, to be used as a lock to gain idempotency features.

After every `POST` request, the response header `X-Idempotency-Status` is returned with the result of the idempotency logic.

This is an opt-in feature. If the header `X-Idempotency-Id` is not present, no extra logic is considered.

The first time the API sees a `X-Idempotency-Id`, it returns `accepted` and locks the value for 15 days.

The following times the same value is used within the first 15 days: the API will return `duplicated`, with status `409`, and will not process the request.

We suggest using a random UUID as `X-Idempotency-Id`.

Summary for situations.

| Request method | `X-Idempotency-Id` | Response status | `X-Idempotency-Status` | Meaning |
| - | - | - | - | - |
| No `POST` | Not checked | As usual | Not present | No extra logic |
| `POST` | Not present | As usual | `missing` | No extra logic |
| `POST` | Present, first time seen | As usual | `accepted` | Lock acquired for 1 day |
| `POST` | Present, already seen in last day | 409 | `duplicated` | Lock was already acquired, this request was already accepted |
| `POST` | Present, but the system cannot check it | 503 | `unavailable` | The intention was acknowledged, but the service cannot check for the idempotency |

**HTTP Response Codes**

| Response code | Meaning |
| - | - |
| `200 OK` | Request completed successfully. See individual endpoints for details of response content. |
| `201 Created` | Request completed successfully, and a resource was created. See individual endpoints for details of response content. |
| `202 Accepted` | Request completed successfully, but not completely processed. See individual endpoints for details of response content. |
| `400 Bad Request` | The request could not be processed due to some error e.g., formatting, parameter or schema validation. See [error message](#error-message) for details of response content. |
| `401 Unauthorized` | Access denied due to authentication failure |
| `403 Forbidden` | Could not complete action due to data constraints. See [error message](#error-message) for details of response content. |
| `404 Not Found` | The requested resource could not be found. See individual endpoints for details of how to identify resources. |
| `409 Conflict` | Request could not be completed. See [error message](#error-message) for details of response content. |
| `429 Too Many Requests` | You have exceeded the rate limit for IP or server. See [rate limiting](#rate-limiting) for details. |
| `502 Bad Gateway` | Internal error. See [error message](#error-message) for details of response content. |
| `503 Service Unavailable` | Internal error. See [error message](#error-message) for details of response content. |
| `504 Gateway Timeout` | Internal timeout. See [error message](#error-message) for details of response content. |
| `422 Unprocessable Entity` | The request could not be completed due to invalid or incorrect data. See [error message](#error-message) for details of response content. |

### Error Message

```json theme={null}
{
    "code": "string",
    "message": "string",
    "details": "string"
}
```

Error messages are presented as JSON objects.

**Error Message Fields**

<ResponseField name="code" type="string">
  A short code for the error
</ResponseField>

<ResponseField name="message" type="string">
  The error message
</ResponseField>

<ResponseField name="details" type="string">
  Error details
</ResponseField>

## Verification of Payee

Due to a regulation regarding Corporate Payments, the Ebury API runs some Verification of Payee validations on beneficiaries when payment requests are done.

Verification of Payee validation checks if the Beneficiary data stored in Ebury (if the request contains a Beneficiary ID) or provided (if the request contains Beneficiary Name, IBAN and Swift Code) matches the actual Bank details. A `full-match` confirms that everything is correct, a `partial-match` lets us know that the data is close but not exactly the same (name varies slightly), and `no-match` warns us that the data is different, which could cause a scam to take place if we go forward with the payment. We also have other error statuses for cases like being unable to find the beneficiary ID provided in our systems, or the system to run the validations is not available at the moment.

When a Single Payment, Multipayment or Mass-Payment request runs the VoP (Verification of Payee) check, two things can happen:

* The VoP result is a `full-match` (for all payments involved), and the payment goes through directly.
* At least one of the payments did not return a `full-match`, and the payment does not go through. The request gets a response with a specific [error message](#error-message) indicating this VoP result.

As part of the VoP response, an `authorization_id` is provided. That `authorization_id` can be used as queryparam on the payment requests to confirm that the customer wants to go with the payment even though the VoP check was not `full-match`. The request itself needs to contain the same data as the previous attempt, only the new `vop_authorization_id` queryparam needs to be added, holding the value of the `authorization_id` received in the previous error response.

If the `vop_authorization_id` queryparam is provided in the Single Payment, Multipayment or Mass-Payment request, the system will check that the value ID matches the one in our database (including the whole request payload received alongside it) and, if it matches, the payment goes through. Otherwise, it returns a specific error explaining that the provided `vop_authorization_id` is not correct.

We have two flags to activate Verification of Payee for customers once they are fully integrated with the new implementation.

* `vop_opted_out` skips Multipayment and Mass-payment (bulk payment) Verification of Payee checks. This is part of the Verification of Payee regulation: any customer that wants to Opt Out of VoP for bulk payments can do so by providing an official signed letter requesting to be opted out of the feature. Apart from this, all customers will be initially opted out, until a grace period, for them to have time to integrate with the new API flow for VoP. After that, any customers that have not provided the `Opt Out` letter will be marked as `false` for `vop_opted_out`. If a customer Opts Out of VoP, no changes will be needed regarding endpoint implementation.
* `vop_skip_single_payments` skip Single Payment (/payments) Verification of Payee checks. This is just a flag we added to give customers some time to integrate with the new API flow for VoP. Initially, all customers will have the flag active. Single Payments cannot be opted out of Verification of Payee according to the regulation, so after the grace period, all API customers will be marked as `false` for `vop_skip_single_payments`.

Regardless of a customer's values for these flags, the `/vop` endpoint will always be available once the feature is launched by October 9, 2025, and therefore will be able to run Verification of Payee checks.

### VoP Error Codes

| Code | Description |
| - | - |
| `VOP_PARTIAL_MATCH` | VoP check returned a `partial-match` |
| `VOP_NO_MATCH` | VoP check returned a `no-match` |
| `VOP_INTERNAL_ERROR` | VoP check could not be done due to an internal error |
| `VOP_VERIFICATION_FAILED` | Provided authorization\_id is invalid for the payment data |

### VoP Check Error Message

The VoP Check not full-match [error message](#error-message) will hold the following information:

<ResponseField name="code" type="string">
  The [VoP error code](#vop-error-codes)
</ResponseField>

<ResponseField name="message" type="string">
  Explains the reason why the system returned a VoP error
</ResponseField>

<ResponseField name="details" type="object">
  The [VoP check details](#vop-check-details)
</ResponseField>

```json theme={null}
{
    "code": "string",
    "details": {
        "authorization_id": "string",
        "results": [
            {
                "message": "string",
                "name": "string",
                "scheme_response_codes": [
                    "string"
                ],
                "status": "string"
            }
        ],
        "status": "string"
    },
    "message": "string"
}
```

### VoP Check Details

<ResponseField name="authorization_id" type="string">
  The ID that can be used as `vop_authorization_id` queryparam to go through with the payments that didn't return a successful `full-match` VoP result.
</ResponseField>

<ResponseField name="results" type="array">
  A list of [vop results](#vop-result), one for each Beneficiary involved in the verification check
</ResponseField>

<ResponseField name="status" type="string">
  Highest priority status received from VoP checks
</ResponseField>

### VoP Result

<ResponseField name="message" type="string">
  General information relating the VoP result
</ResponseField>

<ResponseField name="name" type="string">
  If `partial-match`, the actual Beneficiary name in the Bank data.
</ResponseField>

<ResponseField name="status" type="string">
  The VoP result for the Beneficiary. One of `FULL_MATCH`, `PARTIAL_MATCH`, `NO_MATCH`, `UNABLE_TO_MATCH` or an error.
</ResponseField>

<ResponseField name="scheme_response_codes" type="array">
  List of response scheme codes. Field to have a reference of the VoP error origin.
</ResponseField>
