# 3. Make your first API request

:::note [Required permissions]

You need the following **Platform** [permissions](https://help.revolut.com/business/help/managing-my-business/users-and-employees/setting-permissions/) to be able to configure the Business API:

- **API & Integrations**:
    - [x] **Manage Integrations**
    - [x] **Manage API**
- **Billing**:
    - [x] **View Business**

:::

:::note

These steps apply to both the [Sandbox](https://sandbox-business.revolut.com/) and [Production](https://business.revolut.com/) environments.

:::

To make your first API request, complete the initial setup, following this high-level procedure:

1. [Generate and upload your certificate](#1-add-your-certificate) to authorize your application to access your Revolut Business account via the Business API.
2. [Provide a client assertion JWT](#2-generate-a-client-assertion) to obtain a signing key.
3. [Consent to the application](#3-consent-to-the-application) in the Revolut Business app to obtain an authorization code.
4. [Exchange authorization code for access token](#4-exchange-authorization-code-for-access-token) to authenticate your requests to the Business API.
5. [Try your first API request](#5-try-your-first-api-request) to get a list of all your accounts.

After you complete and validate the setup, you can make requests to the Business API.
If you encounter issues, such as your access token has expired, see the [Troubleshooting](#troubleshooting) section.

## 1. Add your certificate

Authorize your application to access your Revolut Business account via the Business API. To do this, first [generate](#generate-a-private-and-a-public-certificate) your certificate and [upload](#upload-your-certificate) it on the **Business API** settings page of the Revolut Business app.

### Generate a private and a public certificate

To generate the certificates, you need a Command Line Interface (CLI), for example, Terminal on macOS. For Windows, we recommend that you install [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) to run the commands.

:::warning [When running commands in the CLI]

Remember to press **Enter** after pasting each command, to make sure each line is executed.

:::

1. Open your preferred CLI.

2. In your CLI, navigate to the directory in which you want to save the certificates.

   ```shell
   cd <INSERT_DIRECTORY>
   ```

3. To create a private and a public certificate, paste this command in your CLI and press **Enter**.

   ```shell
   openssl genrsa -out privatecert.pem 2048
   openssl req -new -x509 -key privatecert.pem -out publiccert.cer -days 1825
   ```

   You will be asked to enter some details about your organization for the certificate's "Distinguished Name". The prompts below will appear line by line.

   ```shell
   Country Name (2 letter code) []:
   State or Province Name (full name) []:
   Locality Name (eg, city) []:
   Organization Name (eg, company) []:
   Organizational Unit Name (eg, section) []:
   Common Name (eg, fully qualified host name) []:
   Email Address []:
   ```

   Enter each piece of information and press **Enter** until you get back to the command prompt.

   Optionally, you can leave some fields blank.
   However, for the public certificate to be generated successfully, you must enter at least one piece of information, for example, **Country Name**.

:::tip [Expected result]

The certificates are generated and saved to the directory you chose in step 2.

To display the contents of your certificate in your CLI, run:

```shell
cat publiccert.cer
```

Alternatively, you can open the certificate file with a text editor and copy its contents from there.

:::

Copy the whole contents of the public certificate and leave the CLI open. You'll need them both in later steps.

:::note

To amend the information, rerun the steps above to overwrite the certificate with a new one.

:::

### Upload your certificate

To upload the generated certificate, copy and paste the content of `publiccert.cer` into the required field.

1. Go to the API settings.

   - ![Production]

     Log in to the [Revolut Business web app](https://business.revolut.com/), and go to the **Business API** [settings](https://business.revolut.com/settings/apis?tab=business-api).

   - ![Sandbox]

     Log in to [Sandbox](https://sandbox-business.revolut.com/), and go to the **Business API** [settings](https://sandbox-business.revolut.com/settings/apis?tab=business-api).

   :::tip [To open the settings manually]

   In the web app, click on the gear icon in the top right corner.
   Then, go to **APIs** → **Business API**.

   :::

2. In the **API Certificates** section, click **Add API certificate** to add your first certificate.

   If you already have other certificates added, click **Add new**.

3. Copy and paste the content of `publiccert.cer` to the **X509 public key** field.

   :::warning

   Make sure that you copy the whole output, including the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` lines.

   :::

4. Specify the **OAuth redirect URI** (in this example we use: `https://example.com`).

   This is the URL where you are redirected after you consent for the application to access your Revolut Business account.
   You must use the same domain here and in the `iss` parameter provided in [substep 3 of **Generate a client assertion**](#2-generate-a-client-assertion).

   For testing purposes, you only need it to [retrieve the `code` parameter from this URL](#3-consent-to-the-application) later, so you can use any URL.

5. Give your certificate a meaningful title. It will help you later to distinguish it from other certificates.

6. Click **Continue**. This opens the **API Certificate** details with the parameters of your application.

7. Copy the `ClientID` shown in those detail. You will need it in the following steps.

:::note [IP whitelisting]

Optionally, provide a list of IP addresses for additional security.
If provided, only traffic from these IP addresses will be allowed.

Single IP addresses as well as IP pools in CIDR notation are allowed.
Make sure that the IPs you provide are **not** [local (i.e. private) IP addresses](https://www.okta.com/en-sg/identity-101/understanding-private-ip-ranges/).

:::

## 2. Generate a client assertion

In order to grant the consent to your application, you will need to generate a client-assertion JWT (JSON Web Token) which is cryptographically signed with your private certificate generated in [step 1](#generate-a-private-and-a-public-certificate).

This JWT will be used whenever a new access token needs to be requested, and it is composed of a header, a payload and a signature.

:::details [See examples for the header and the payload, and the format of the parameters]

**JWT Header:**

```json
{
  "alg": "RS256",
  "typ": "JWT"
}
```

**JWT Payload:**

```json
{
  "iss": "example.com",
  "sub": "zfTKV9Eie_XLHl03f2Y7vJYDe5Klr6Y54v8fsp4Gvgp",
  "aud": "https://revolut.com",
  "exp": 1706136791
}
```

The payload parameters must meet the following format:

| Field | Description                                                                                                                                                       | Format                                             | Required |
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | -------- |
| `iss` | Domain from your [OAuth redirect URI (provided in substep 4 of **Upload your certificate**)](#upload-your-certificate) without `https://`.                        | String                                             | Yes      |
| `sub` | Your `client_id`                                                                                                                                                  | String                                             | Yes      |
| `aud` | `https://revolut.com`                                                                                                                                             | String                                             | Yes      |
| `exp` | JWT expiration date. Example for 90 days in the future: `1706136791`. You can use [this converter](https://www.unixtimestamp.com/) to provide the UNIX timestamp. | Number, in UNIX timestamp format, given in seconds | Yes      |

:::warning

Do not provide the `exp` value as a string!

:::

There are several libraries to generate a JWT.
To manually generate the JWT from your CLI, perform the following steps:

1. Go back to your CLI.

   Make sure that you are in the same directory where you saved your `privatecert.pem` file from [step 1](#generate-a-private-and-a-public-certificate).
   If you want to use a different directory, make sure that you copy the private certificate there.

2. Prepare the **JWT header** and save it as `header.json`.

   - ![Save with a CLI command]

     Paste this command into your CLI and press **Enter**.

     ```shell
     cat <<EOF > header.json
     {
       "alg": "RS256",
       "typ": "JWT"
     }
     EOF
     ```

   - ![Save manually]

     Alternatively, copy the following contents to a text editor and save it as a JSON file.

     ```json
     {
       "alg": "RS256",
       "typ": "JWT"
     }
     ```

3. Prepare the **JWT payload** and save it as `payload.json`.
   Replace the placeholders in angle brackets (`<>`) with [your own values](#upload-your-certificate).
   Also, make sure that the `exp` value is a number, not a string.

   - ![Save with a CLI command]

     Replace the placeholders in the payload below with your own values, then copy and paste the command into your CLI and press **Enter**.

     ```shell
     cat <<EOF > payload.json
     {
       "iss": "<your domain without https://>",
       "sub": "<your client_id>",
       "aud": "https://revolut.com",
       "exp": <unix timestamp in seconds>
     }
     EOF
     ```

   - ![Save manually]

     Replace the placeholders in the payload below with your own values, then copy the content to a text editor and save it as a JSON file.

     ```json
     {
       "iss": "<your domain without https://>",
       "sub": "<your client_id>",
       "aud": "https://revolut.com",
       "exp": 1706136791
     }
     ```

4. Generate the signed JWT and save it as `client_assertion.txt`.

   Run this set of commands in your CLI. Remember to press **Enter** after pasting, otherwise it might not run fully, resulting in the signature missing.

   ```bash
   # Add encoded and normalised header
   cat header.json | tr -d '\n' | tr -d '\r' | openssl enc -base64 -A | tr +/ -_ | tr -d '=' > client_assertion.txt
   echo -n "." >> client_assertion.txt
   # Add encoded and normalised payload
   cat payload.json | tr -d '\n' | tr -d '\r' | openssl enc -base64 -A | tr +/ -_ | tr -d '=' >> client_assertion.txt
   # Generate signature
   cat client_assertion.txt | tr -d '\n' | tr -d '\r' | openssl dgst -sha256 -sign privatecert.pem | openssl enc -base64 -A | tr +/ -_ | tr -d '=' > sign.txt
   echo -n "." >> client_assertion.txt
   # Add signature
   cat sign.txt >> client_assertion.txt
   ```

:::tip [Expected result]

A `client_assertion.txt` file containing the client assertion JWT is created in your chosen directory.

To display the contents of your JWT in the CLI, run:

```shell
cat client_assertion.txt
```

:::danger

Never share your client-assertion JWT (JSON web token) and `refresh_token` with anyone, as these can be used to access your banking data and initiate transactions.

:::

:::tip [Validate the result]

You can validate the JWT that you just generated at [jwt.io](https://jwt.io):

1. Paste the contents of the `client_assertion.txt` file into the **Encoded Token** field.
2. In the **JWT Signature Verification** section, paste the contents of the `publiccert.cer` file into the **Public Key** field.
3. Check that the JWT is decoded correctly and the signature is verified.

:::note

This only checks if the JWT is well-formed, if the header and payload have been correctly signed, and if the signature matches the provided certificate.

:::

## 3. Consent to the application

1. Go back to the API settings.

   - ![Production]

     Log back in to the [Revolut Business web app](https://business.revolut.com/), and go to the **Business API** [settings](https://business.revolut.com/settings/api) again.

   - ![Sandbox]

     Log back in to [Sandbox](https://sandbox-business.revolut.com/), and go to the **Business API** [settings](https://sandbox-business.revolut.com/settings/api) again.

2. Select the certificate you want to edit.

4. In the **API Certificate** details, copy your client ID from the **ClientID** field.

5. Click **Enable access**. This takes you to the `/app-confirm` endpoint where you grant your application access to your account via the Business API. See an example below.

   ```shell
   https://business.revolut.com/app-confirm?client_id=<ClientID>&redirect_uri=https://example.com&response_type=code
   ```

   :::tip [Optionally]

   You can narrow down the security permissions of the consent by adding to this URL the `scope` query parameter with a comma separated list of the desired [scopes](/docs/guides/manage-accounts/get-started/make-your-first-api-request#3-get-an-access-token) as its value.
   For example: `(...)&response_type=code&scope=READ,WRITE`.

   :::

5. Click **Authorise**. This triggers a 2-factor authentication (2FA) process.

   ![Account access request](/img/manage-accounts/account-access-request-transparent.png)

   On successful authorisation, you are redirected to the `OAuth redirect URI` that you specified.

6. Get the authorization code (`code`) from the redirect URI.

   ```uri
   https://example.com?code=oa_prod_vYo3mAI9TmJuo2_ukYlHVZMh3OiszmfQdgVqk_gLSkU
   ```

   :::warning

   For security reasons, the authorization code is only valid for two minutes.

   If it expires before you use it, you must repeat the [substeps 4-6](#3-consent-to-the-application) of **Consent to the application** to generate a new one.

   :::

## 4. Exchange authorization code for access token

To exchange the authorization code (`code`) for an [access token](/docs/guides/manage-accounts/get-started/make-your-first-api-request#3-get-an-access-token) (`access_token`), make the following cURL call, for example, from your CLI:

- ![Production]

  ```shell
  curl https://b2b.revolut.com/api/1.0/auth/token \
    -H "Content-Type: application/x-www-form-urlencoded"\
    --data "grant_type=authorization_code"\
    --data "code=<INSERT_AUTHORIZATION_CODE>"\
    --data "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer"\
    --data "client_assertion=<INSERT_JWT>"
  ```

- ![Sandbox]

  ```shell
  curl https://sandbox-b2b.revolut.com/api/1.0/auth/token \
    -H "Content-Type: application/x-www-form-urlencoded"\
    --data "grant_type=authorization_code"\
    --data "code=<INSERT_AUTHORIZATION_CODE>"\
    --data "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer"\
    --data "client_assertion=<INSERT_JWT>"
  ```

:::details [Request fields (URL-encoded) - details]

See what the particular fields mean, how their values should be formatted, and whether they must be included in the request or if they can be skipped:

| Field                   | Description                                                                                       | Format | Required |
| ----------------------- | ------------------------------------------------------------------------------------------------- | ------ | -------- |
| `grant_type`            | The OAuth grant type: `authorization_code`.                                                       | String | Yes      |
| `code`                  | The authorization code obtained from the redirect URI in [step 3](#3-consent-to-the-application). | String | Yes      |
| `client_assertion_type` | The type of the client assertion: `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`.       | String | Yes      |
| `client_assertion`      | The JWT token that you generated in [step 2](#2-generate-a-client-assertion).                     | String | Yes      |

:::

:::tip [Expected result]

A successful response returns the access token with its type and expiry time in seconds, and the refresh token, which does not expire.

```json
{
  "access_token": "oa_prod_rPo9OmbMAuguhQffR6RLR4nvmzpx4NJtpdyvGKkrS3U",
  "token_type": "bearer",
  "expires_in": 2399,
  "refresh_token": "oa_prod_hQacSGnwx-luIfj3dlVByrytVV9rWAnyHkpJTwG_Tr8"
}
```

:::note

Every access token is only valid for 40 minutes.
After the access token expires, you must request a new one, using the refresh token (`refresh_token`) and the JWT (`client_assertion.txt`).

For more information, see the [Troubleshooting](/docs/guides/manage-accounts/get-started/make-your-first-api-request#troubleshooting) section: [Access token expired](#access-token-expired).

:::

## 5. Try your first API request

To verify that everything is working, make a request to the [`/accounts`](/docs/api/business#get-accounts) endpoint to get a list of all your accounts using the access token which you obtained in the previous step:

- ![Production]

  ```shell
  curl https://b2b.revolut.com/api/1.0/accounts \
    -H "Authorization: Bearer <your access_token>"
  ```

- ![Sandbox]

  ```shell
  curl https://sandbox-b2b.revolut.com/api/1.0/accounts \
    -H "Authorization: Bearer <your access_token>"
  ```

On success, you get a response similar to this one:

```json
[
  {
    "id": "2a0d4d03-e26c-4159-9de1-c6bf3adfd8a1",
    "name": "Current GBP account",
    "balance": 100.0,
    "currency": "GBP",
    "state": "active",
    "public": false,
    "updated_at": "2017-06-01T11:11:11.1Z",
    "created_at": "2017-06-01T11:11:11.1Z"
  },
  {
    "id": "df8d6b20-0725-482e-a29e-fb09631480cf",
    "name": "EUR expenses account",
    "balance": 1234.0,
    "currency": "EUR",
    "state": "active",
    "public": false,
    "created_at": "2017-06-01T11:11:11.1Z",
    "updated_at": "2017-06-01T11:11:11.1Z"
  }
]
```

:::tip [Success]

Congratulations! You're ready to make requests to the Business API with an access token.

:::

## Troubleshooting

### Access token expired

When the access token expires, you won't be able to make successful API calls and will get the `401 Unauthorized` error in response:

```json
{"message":"The request should be authorized."}
```

To regain access, you must first request a new access token using the refresh token (returned in [step 4](#4-exchange-authorization-code-for-access-token)) and the JWT (obtained in [step 2](#2-generate-a-client-assertion)).

#### Get a new access token

To request a new access token, make the following request.

:::warning

Refreshing an access token invalidates the previous token.

This means that if you have an access token that hasn't expired yet and you refresh it with your refresh token, your existing access token will no longer work, and you will need to switch to the new access token.

:::

- ![Production]

  ```shell
  curl https://b2b.revolut.com/api/1.0/auth/token \
    -H "Content-Type: application/x-www-form-urlencoded"\
    --data "grant_type=refresh_token"\
    --data "refresh_token=<insert refresh_token>"\
    --data "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer"\
    --data "client_assertion=<insert JWT>"
  ```

- ![Sandbox]

  ```shell
  curl https://sandbox-b2b.revolut.com/api/1.0/auth/token \
    -H "Content-Type: application/x-www-form-urlencoded"\
    --data "grant_type=refresh_token"\
    --data "refresh_token=<insert refresh_token>"\
    --data "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer"\
    --data "client_assertion=<insert JWT>"
  ```

:::details [Request fields (URL-encoded) - details]

See what the particular fields mean, how their values should be formatted, and whether they must be included in the request or if they can be skipped:

| Field                   | Description                                                                                 | Format | Required |
| ----------------------- | ------------------------------------------------------------------------------------------- | ------ | -------- |
| `grant_type`            | The OAuth grant type: `refresh_token`.                                                      | String | Yes      |
| `refresh_token`         | The refresh token.                                                                          | String | Yes      |
| `client_assertion_type` | The type of the client assertion: `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`. | String | Yes      |
| `client_assertion`      | The JWT token that you generated in [step 2](#2-generate-a-client-assertion).               | String | Yes      |

:::

A successful response returns a new access token with its type and expiry time in seconds.

```json
{
  "access_token": "oa_prod_rPo9OmbMAuguhQffR6RLR4nvmzpx4NJtpdyvGKkrS3U",
  "token_type": "bearer",
  "expires_in": 2399
}
```

### Client assertion JWT expired

Similarly to the access token, when you create a JWT in [step 2](#2-generate-a-client-assertion), it also has an expiration date specified in the `exp` field.

If you refresh the access token with an expired JWT, you will get an error response like this:

```json
{
    "error": "invalid_request",
    "error_description": "The Token has expired on Mon Jun 03 15:55:21 UTC 2024."
}
```

In this case, you must generate a new JWT by following the instructions in [step 2](#2-generate-a-client-assertion).
Make sure to use the same private key as before.

:::tip [Best practices]

For security reasons, it is recommended that you provide a short expiration date when generating the JWT, just enough to refresh the `access_token`.
This reduces the risk in the event the JWT gets leaked.

:::

## What's next

- See the [API Reference](/docs/api/business) for full details on different endpoints of the Business API.
- Browse our guides to learn how to use various Business API endpoints.
- See the [API usage and testing](/docs/guides/manage-accounts/api-usage-and-testing/errors) section for developer-specific details.