# Register as a TPP

This guide will walk you through the steps to register as a **Third-Party Provider (TPP)** with bunq. If you're a PSD2-certified company, you'll learn how to authenticate with the bunq API, register your certificate, and start using your credentials to access the data and services you're authorized for.

{% hint style="info" %}

## **Pre-Requisite**

* You must generate a **2048-bit RSA key pair** beforehand.
* You'll need your **QSeal certificate** (Qualified Seal Certificate), including intermediate and root certificate chain.
* OpenSSL and curl installed
* Basic knowledge of command line
  {% endhint %}

The easiest way to register is via command line, but you can also implement this in your code.

{% hint style="success" %}
If you're in a hurry and just want to test in the Sandbox environment. We bundled all commands on this page into 1 bash script that creates a PSD2 user and registers it for you. You'll just get an API key for that created user that you can use for the rest of your implementation

Although a script provides you with a quick way to get started, we do recommend you read this page thorougly before moving into production.&#x20;

[Link to script](https://github.com/two-trick-pony-NL/PSD2-Implementation-for-bunq-API/blob/main/create_psd2_user.sh)
{% endhint %}

### 🛠️ Step-by-Step Integration

#### 1. Generate Installation Key Pair

These keys are used to register your app installation with bunq:

```bash
# Public key: installation.pub
# Private key: installation.key
openssl genrsa -out installation.key && openssl rsa -in installation.key -outform PEM -pubout -out installation.pub
```

***

#### 2. (Sandbox only) Generate a test PSD2 Certificate

{% hint style="info" %}
The certificate is not validated in sandbox, so you can create as many as you need. Just make sure to use the qSEAL certificate when moving to production.
{% endhint %}

Replace the subject `/CN=.../C=...` with your own details as needed.

```bash
# Certificate: psd2.cert
# Private key: psd2.key
openssl req -x509 -newkey rsa:4096 -keyout psd2.key -out psd2.cert -days 365 -nodes -subj "/CN=Test PISP AISP $(uuidgen)/C=NL"
```

***

#### 3. Create Installation

This step registers your public key with bunq and returns an installation token.

The `client_public_key` is the **public part of the key pair** you generated earlier using OpenSSL. This key is sent to bunq so we know how to verify future requests from your integration.

What the API expects here is:

* A **PEM-formatted public key** (typically starting with `-----BEGIN PUBLIC KEY-----` and ending with `-----END PUBLIC KEY-----`)
* All line breaks and formatting preserved correctly as a single escaped string (so it fits into the JSON payload)

Here's an example of a formatted public key:

```json
"-----BEGIN PUBLIC KEY-----\nMIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBKvVUm/gMi7NmTQImtpX1K\nTFMy3BQPvi6uYWMIIy/YHlZNGZbseKyo/dSa22VnFitjoJAt1S6iy04iuiYKCo4p\nUT9jNhn+JW7+U5Ptia6Y1yDwAqioeuL90suO6XLk35Vj7uuyXxlZO3u79/nPJrmp\nmYx2kEhEEISVWd9+TAFrFjImdGVd6DXK4d3D8/tH4GwILcmL7PbigbFLjeCVbkUi\nFqSiMgtQJkHVHhwedwLehuNg/oL3MRBw1bIxrYnjpO6qfyWoYNmCKYo3KgZYrQZ8\nVUjD0bpyfZEWX3+c849nemRdDa8eUZqjzneV2P/m96iiLWbve5KKSklSz2UtCecD\nAgMBAAE=\n-----END PUBLIC KEY-----\n"
```

You can call the installation endpoint by using the following command line:

```bash
INSTALLATION=$(curl -X POST https://public-api.sandbox.bunq.com/v1/installation \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-Bunq-Client-Request-Id: $(uuidgen)" \
--data "{\"client_public_key\": \"$(awk 'NF {sub(/\r/, ""); printf "%s\\n", $0;}' installation.pub)\"}")
```

Here is the full specification of the endpoint:

{% openapi src="<https://346554585-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGE9Y1hc6C24r4Hen6KFH%2Fuploads%2FIUa888wk2qwhos5DXTS3%2Fswagger.json?alt=media&token=020e751b-2a4b-4993-8247-1f0b9fab0bf5>" path="/installation" method="post" %}
[swagger.json](https://346554585-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGE9Y1hc6C24r4Hen6KFH%2Fuploads%2FIUa888wk2qwhos5DXTS3%2Fswagger.json?alt=media\&token=020e751b-2a4b-4993-8247-1f0b9fab0bf5)
{% endopenapi %}

***

#### 4. Generate the Signature

This proves ownership of your PSD2 certificate by signing the public key and token.

{% hint style="warning" %}
Make sure there is NO new line at the end of the file! Otherwise, the signature will be invalid.
{% endhint %}

1. Extract the `installation_token` from the previous step:

```bash
TOKEN=$(echo $INSTALLATION | grep -o '"token":"[A-Za-z0-9]*"' | cut -d '"' -f 4)
echo -n $TOKEN > installation.token
```

2. Take the `server_public_key` of the installation you also received in the previous step.
3. Append the token
4. Sign the string using the private key of your PSD2 certificate

```bash
openssl dgst -sign psd2.key -keyform PEM -sha256 -out signature <(cat installation.pub installation.token)
```

This base64 string should be passed as value of `client_public_key_signature` in the next step.

***

#### 6. Create Payment Service Provider Credential

{% hint style="success" %}
Need to update your PSD2 certificate? No problem, just repeat this step with the new certificate :smile:
{% endhint %}

Use your certificate and signature to request your TPP credentials in bunq's API.

Here is the command line code:

```bash
CREDENTIAL=$(curl -X POST https://public-api.sandbox.bunq.com/v1/payment-service-provider-credential \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-Bunq-Client-Request-Id: $(uuidgen)" \
-H "X-Bunq-Client-Authentication: $TOKEN" \
--data "{
  \"client_payment_service_provider_certificate\": \"$(awk 'NF {sub(/\r/, ""); printf "%s\\n", $0;}' psd2.cert)\",
  \"client_payment_service_provider_certificate_chain\": \"$(awk 'NF {sub(/\r/, ""); printf "%s\\n", $0;}' psd2.cert)\",
  \"client_public_key_signature\": \"$(cat signature | base64)\"
}")
```

{% hint style="warning" %}
You can repeat the same value on the `client_payment_service_provider_certificate` and `client_payment_service_provider_certificate_chain` in sandbox, but please **ensure you have your certificate chain ready when going to production**, otherwise the call will fail.
{% endhint %}

Save the response for the next step:

```bash
echo $CREDENTIAL > credential.json
```

Here is the full specification of the endpoint:

{% openapi src="<https://346554585-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGE9Y1hc6C24r4Hen6KFH%2Fuploads%2FIUa888wk2qwhos5DXTS3%2Fswagger.json?alt=media&token=020e751b-2a4b-4993-8247-1f0b9fab0bf5>" path="/payment-service-provider-credential" method="post" %}
[swagger.json](https://346554585-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGE9Y1hc6C24r4Hen6KFH%2Fuploads%2FIUa888wk2qwhos5DXTS3%2Fswagger.json?alt=media\&token=020e751b-2a4b-4993-8247-1f0b9fab0bf5)
{% endopenapi %}

{% hint style="warning" %}
It's good to know that if you register as a Payment Server Provider you will not be a 'typical' USER\_PERSON or USER\_COMPANY ( [bunq-api-objects](https://doc.bunq.com/basics/bunq-api-objects "mention")). Instead you'll become a UserPaymentServiceProvider - this is a special kind of user that only serves as a user for your OAuth installation. This user type will not have any bank accounts or payments of it's own. You can only manage that user through our API&#x20;
{% endhint %}

***

#### 7. Extract Credential Token and register Your Device

After creating a credential in the previous step, you'll receive a `credential_token`, which acts as a unique secret. This token is required when registering your device using `POST /device-server`. Registering the device is an important security step—it lets bunq know *where* the API calls are coming from and links your setup to a specific environment (like your server or app).&#x20;

By sending the credential token as the `secret`, you're proving that your device is authorized to operate under your PSD2 certificate and credentials. Without this step, bunq can’t associate API activity with a verified, trusted source.

Here's the bash code to extract your `credential_token` from the **credential.json** (saved in the last step):

```bash
CREDENTIAL_TOKEN=$(cat credential.json | grep -o '"token_value":"[A-Za-z0-9]*"' | cut -d '"' -f 4)
```

With that value in hands, you can then call the endpoint `POST /device-server` and register your device:

```bash
curl -X POST https://public-api.sandbox.bunq.com/v1/device-server \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-Bunq-Client-Request-Id: $(uuidgen)" \
-H "X-Bunq-Client-Authentication: $TOKEN" \
--data "{\"secret\":\"$CREDENTIAL_TOKEN\", \"description\": \"My server\"}"
```

{% hint style="warning" %}

#### IP addresses

When using a standard API Key the DeviceServer and Installation that are created in this process are bound to the IP address they are created from.&#x20;

Using a Wildcard API Key gives you the freedom to make API calls from any IP address after the POST device-server. You can switch to a Wildcard API Key by tapping on “Allow All IP Addresses” in your API Key menu inside the bunq app.&#x20;

You can also programatically switch to a Wildcard API Key by passing your current ip and a `*` (asterisk) in the `permitted_ips` field of the device-server POST call. E.g: `["1.2.3.4", "*"]`.
{% endhint %}

Here is the full specification of the endpoint:

{% openapi src="<https://346554585-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGE9Y1hc6C24r4Hen6KFH%2Fuploads%2FIUa888wk2qwhos5DXTS3%2Fswagger.json?alt=media&token=020e751b-2a4b-4993-8247-1f0b9fab0bf5>" path="/device-server" method="post" %}
[swagger.json](https://346554585-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGE9Y1hc6C24r4Hen6KFH%2Fuploads%2FIUa888wk2qwhos5DXTS3%2Fswagger.json?alt=media\&token=020e751b-2a4b-4993-8247-1f0b9fab0bf5)
{% endopenapi %}

***

#### 9. Sign the Session Request

We are legally required to protect our users and their data from malicious attacks and intrusions. That is why we beyond having a secure https connection, we use [asymmetric cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography) for signing requests that create a session or payment. The use of signatures ensures the data is coming from the trusted party and was not modified after sending and before receiving.

In this step, you're preparing to create a **session** with bunq's API, which requires proving your identity using a digital signature.

```bash
SESSION_REQUEST_BODY="{\"secret\":\"$CREDENTIAL_TOKEN\"}"
echo -n $SESSION_REQUEST_BODY > session.request
```

{% hint style="warning" %}
Make sure there is NO new line at the end of the file! Otherwise, the signature will be invalid.
{% endhint %}

Then you'll digitally **sign the contents** of the request body using your **installation private key**. This proves to bunq that the request really comes from someone in control of the private key tied to your public key.

After, you'll encode the binary signature into base64 so it can be safely sent in the signature HTTP header from the next step.

```bash
openssl dgst -sign installation.key -keyform PEM -sha256 -out signature < session.request
SESSION_REQUEST_SIGNATURE=$(cat signature | base64)
```

{% hint style="success" %}

## Troubleshooting

If you get an error telling you "The request signature is invalid", please check the following:

* There are no redundant characters (extra spaces, trailing line breaks, etc.) in the data to sign.
* Make sure the body is appended to the data to sign exactly as you're adding it to the request.
* You have added the full body to the data to sign.
* You use the data to sign to create a SHA256 hash signature.
* You have base64 encoded the SHA256 hash signature before adding it to the request under `X-Bunq-Client-Signature`.

You can find more info about signing the request body in [this link here](https://doc.bunq.com/basics/signing).
{% endhint %}

***

#### 11. Create Session&#x20;

The `POST /session-server` endpoint is used to **start a new session** with the bunq API. Once your device is registered and you've created a valid credential, this call creates an authenticated session. The session ensures secure, time-limited access to the bunq API on behalf of your registered device and credentials.

```bash
curl -X POST https://public-api.sandbox.bunq.com/v1/session-server \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-Bunq-Client-Request-Id: $(uuidgen)" \
-H "X-Bunq-Client-Signature: $SESSION_REQUEST_SIGNATURE" \
-H "X-Bunq-Client-Authentication: $TOKEN" \
--data "$SESSION_REQUEST_BODY"
```

{% hint style="success" %}
The response will contain a `session_token`. Use this token in the `X-Bunq-Client-Authentication` header for all subsequent API calls.
{% endhint %}

Here is the full specification of the endpoint:

{% openapi src="<https://346554585-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGE9Y1hc6C24r4Hen6KFH%2Fuploads%2FIUa888wk2qwhos5DXTS3%2Fswagger.json?alt=media&token=020e751b-2a4b-4993-8247-1f0b9fab0bf5>" path="/session-server" method="post" %}
[swagger.json](https://346554585-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGE9Y1hc6C24r4Hen6KFH%2Fuploads%2FIUa888wk2qwhos5DXTS3%2Fswagger.json?alt=media\&token=020e751b-2a4b-4993-8247-1f0b9fab0bf5)
{% endopenapi %}

***

### ✅ You're Ready!

You’ve now successfully authenticated with the bunq Public API as a PSD2-certified provider.&#x20;

Now you're ready to set up the OAuth with your end user and start using the API in accordance with your certified roles (AISP, PISP, or CBPII).

Please refer to this page on how to set up OAuth:

{% content-ref url="../../basics/authentication/oauth" %}
[oauth](https://doc.bunq.com/basics/authentication/oauth)
{% endcontent-ref %}

{% hint style="info" %}

## 📝 **Reminder**&#x20;

All integration steps must be repeated in the **production environment** with your real eIDAS certificate when you're ready to go live.
{% endhint %}

Ready to continue? You can check what you can do with bunq's API according to your role in these pages here:

<table data-view="cards"><thead><tr><th></th></tr></thead><tbody><tr><td><a data-mention href="../account-information-service-provider-aisp">account-information-service-provider-aisp</a></td></tr><tr><td><a data-mention href="../payment-initiation-service-provider-pisp">payment-initiation-service-provider-pisp</a></td></tr><tr><td><a data-mention href="../card-based-payment-instrument-issuer-cbpii">card-based-payment-instrument-issuer-cbpii</a></td></tr></tbody></table>
