# Bank Details Access

Source: https://docs.gocardless.com/docs/gc-embed/bank-details-access

# Bank Details Access

> **Info:**
> To opt-in to bank details access, please contact your account manager to enable this upgrade.

The `/bank_account_details` endpoint allows you to access encrypted bank account details by bank account identifier.

This document outlines the steps required to retrieve customer bank account details. The process consists of two key steps:

1. **One-Time Public Key Setup:** Generate your public/private key pair and upload your public key in the GoCardless dashboard. This setup is a one-time action.
2. **Retrieving Bank Account Details:** Use the GoCardless API to retrieve encrypted bank account details and decrypt the response to get the customer's plaintext bank details.

## Public Key Setup

The public key provided to GoCardless, must be an RSA 2048 bit key. This will be used to encrypt the symmetric encryption key using the OAEP padding scheme.

Please follow the steps outlined below to generate your public/private key pair and add your public key in the GoCardless dashboard.

### Generating your key pair

1. Generate an RSA-2048 public/private key pair

```bash
openssl genrsa -out privatekey.pem 2048
openssl rsa -in privatekey.pem -pubout -out publickey.pem
```

2. Securely store your private key
3. Copy your public key PEM

### Upload your public key

To make your public key available to GoCardless you will need to upload your public key in the dashboard:

1. Go to[ Developers](https://manage.gocardless.com/developers).
2. Under **Create** 1. Click **Public Key**
3. Click **Add public key** 1. Enter a **Name** - This will help you to identify your public key in the dashboard
4. Enter your **Public key** - Paste your generated public key PEM into the input box
5. Click **Add**
6. **Copy** **and save **the generated **Key ID** - You will need to use this ID to tell GoCardless which public key to use to encrypt data when making requests to the `/bank_account_details` endpoint
7. Click **Continue**

> **Info:**
> You can revoke a public key at any time by deleting it from the [Public
>   Keys](https://manage.gocardless.com/developers/public-keys) page.

## Steps to Retrieve Bank Details

You can retrieve customer bank account details by:

- providing any [customer bank account ID](/docs/api-reference/customer-bank-account) to the **/bank_account_details** API
- including the public key ID you saved earlier as a request header `GC-Key-Id: PK123`

If you want to retrieve customer bank details after they have completed a billing request, you can use the [webhook notification](/docs/gc-embed/handling-webhooks) GoCardless sends upon fulfilment of a billing request to retrieve the bank details by following these steps:

1. Get the bank account identifier from the fulfilment webhook - You will find this in `links.customer_bank_account` of the fulfilled billing request object
2. Send a request to the bank account details endpoint\*\* **using the customer bank account identifier - **GET /bank_account_details/\\*\*

```json
REQUEST
GET /bank_account_details/BA123

HEADERS
- Gc-Key-Id (required): the key ID of your uploaded public key to use for encryption.

RESPONSES

success - 200: return payload containing encrypted bank details:

error - 403: integrator doesn't have the required feature enabled:
	- Forbidden request

error - 404: bank account doesn't exist:
	- Resource not found

error - 422: failure to fetch or encrypt the bank details:
    - Ensure your Gc-Key-Id header contains a valid public key ID

```

### Decrypting bank details

The response body from the **/bank_account_details** endpoint will contain an encrypted payload following [Json Web Encryption (JWE) standards described in RFC7516.](https://www.rfc-editor.org/rfc/rfc7516)

The JWE Flattened format payload follows the structure shown in the example below and all values including the nested json header are base64 URL safe encoded.

Example response body from `/bank_account_details/\`:

```json

}
```

The bank account details are encrypted using hybrid encryption, combining RSA and AES algorithms. The JWE payload includes a symmetric AES key, encrypted using your RSA public key, and a ciphertext, encrypted using the symmetric key.

Nested JWE payload properties explained:

| Key           | Value                                                                                                                                                                                                                                                                                                                                                                                |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| protected     | Protected header values, including: `alg`: the asymmetric encryption type used to encrypt the symmetric key `enc`: the symmetric encryption type used to encrypt the actual content (bank details) `kid`: key ID of the public key used for asymmetric encryption The strings included in the protected header follow the standard set in RFC7518 for `alg` values and `enc` values. |
| encrypted_key | Random symmetric key, encrypted with your public key. The symmetric key generated for each request will be an AES 256 bit key, used to encrypt the data in Galois/Counter Mode (GCM).                                                                                                                                                                                                |
| iv            | Initialisation vector - an arbitrary random number for seeding encryption.                                                                                                                                                                                                                                                                                                           |
| ciphertext    | Actual data (bank details) encrypted with the symmetric key                                                                                                                                                                                                                                                                                                                          |
| tag           | Value to be recomputed on decrypt to validate the authenticity of the payload                                                                                                                                                                                                                                                                                                        |

#### Decryption Overview

The process to decrypt the received payload will consist of the following broad steps

- Use the `public_key_id` to verify the identity of your key pair
- Use your private key to decrypt the `encrypted_key`
- Use the now decrypted `encryption_key` to decrypt the `ciphertext`

#### Detailed Decryption Steps

Many widely available public libraries exist to handle JSON Object Signing and Encryption (JOSE), and will be able to decrypt the response. You can also choose to handle decryption yourself.

> **Warning:**
> Before implementing the decryption yourself, there are two points you need to be aware of:
> 
> - In most implementations, the decryption operation will infer the correct cryptographic methods (in our case RSA-OAEP and A256GCM) from the protected header values.
> - Authenticated methods such as GCM also make use of Additional Authenticated Data (AAD) which is used to compute the tag value in a JWE payload — by default the protected header is included as AAD by the JOSE library used in the following example which follows recommendations in the RFC specification.

The following is an example of how you could use a [Javascript JOSE library](https://github.com/panva/jose) to decrypt bank details:

```javascript

/**
 * Decryption method
 * @param responseBody  response body from /bank_account_details
 * @param privateKeyPEM PEM-encoded private key string
 * @param keyId  key ID of the public JWK you expect GoCardless to have encrypted the response with
 */
async function decrypt(responseBody, privateKeyPEM, keyId)  = await jose.flattenedDecrypt(JwePayload, private_key);

  // Decode the decrypted data.
  const decoder = new TextDecoder();

  // Pretty print the plaintext data.
  console.log(JSON.stringify(JSON.parse(decoder.decode(plaintext)), null, 2));
}
```

The following is an example of how you could handle decryption without a JOSE library, in Ruby using OpenSSL:

```ruby
# response_body: GET /bank_account_details response body
# private_key_pem: your private key PEM-encoded string
# kid: the key ID of the public JWK you expect GoCardless to have encrypted the response with
def decrypt(response_body:, private_key_pem:, kid:)

  # decode the response body
  jwe_payload = Hash[ response_body["bank_account_details"].map do |key, value|
    [key, Base64.urlsafe_decode64(value)]
  end]

  # parse the protected_header
  protected_header = JSON.parse(jwe_payload["protected"])
  key_management_algorithm = protected_header["alg"]
  content_encryption_algorithm = protected_header["enc"]

  # check if protected_header kid matches expected kid
  raise StandardError if protected_header["kid"] != kid

  private_key = OpenSSL::PKey.read(private_key_pem)

  # decrypt the symmetric key
  content_encryption_key = private_key.private_decrypt(jwe_payload["encrypted_key"],
                                                       padding_value(key_management_algorithm))

  # instantiate a cipher and set the algorithm type to that contained in the jwe protected header's "enc" value
  decipher = OpenSSL::Cipher.new(cipher_value(content_encryption_algorithm))

  decipher.decrypt # set cipher to decryption mode

  decipher.key = content_encryption_key
  decipher.iv = jwe_payload["iv"]
  decipher.auth_data = Utils.b64_encode(JSON.generate(protected_header))
  decipher.auth_tag = jwe_payload["tag"]

  plaintext = decipher.update(jwe_payload["ciphertext"]) + decipher.final

  JSON.parse(plaintext) # => should return the decrypted object containing either local bank account details or an IBAN
end

```

#### Interpreting the decrypted bank details

The decrypted bank details will be in [IBAN](/docs/api-reference/local-bank-details) format in countries where IBAN is supported, or the [local bank details](/docs/api-reference/local-bank-details) otherwise.

IBAN format:

```json

```

Local bank details format:

```json

```

## What's next?

  
#### [API Reference](/docs/api-reference)

Full reference documentation for all GoCardless API endpoints.

  
#### [Go Live](https://get.gocardless.com/l/1118633/2026-03-26/skf8xj)

Ready to launch? Contact the GoCardless Embed team to take your integration live.