The digital banking service bunq has an API for their banking accounts. With support for webhooks, you can easily track any activity within your account! bunq calls them Callbacks. This guide explains how to use the bunq mobile application and some cURL requests to get a JSON request for every activity in your bunq banking accounts.
Create a bunq Callback
Other services call them webhooks, bunq calls them Callbacks! A callback consists of an endpoint URL and a type of event. The available types are listed in the API documentation about Callbacks. For tracking the basic account activity, this example will create two Callbacks: one for PAYMENT
and one for MUTATION
events.
This guide requires a paid bunq account and involves these acitivities:
- Use the bunq mobile application
- Create a
private
/public
key with OpenSSL - Send simple HTTPS requests with cURL
You can do this!
Create Private Key
To use the bunq API, you first need to generate a typical encryption key pair. The private key is used to create a signature of every API request body; the public key will be uploaded to bunq, so every request’s signature can be verified.
# Create a private key
$ > openssl genrsa -des3 -out bunq.pem 2048
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
# Create a public key
$ > openssl rsa -in bunq.pem -outform PEM -pubout -out bunq_public.pem
Enter pass phrase for bunq.pem:
writing RSA key
Register API Client Installation
First, you need to send the public key to the bunq API. This will register your key and allow further API communication.
# Create single line key string
$ > export KEY_DATA=`cat bunq_public.pem`
$ > export KEY=${KEY_DATA//$'\n'/\\n}
$ > curl -X POST https://api.bunq.com/v1/installation \
-H "Content-Type: application/json" \
-H "Cache-Control: no-cache" \
-H "User-Agent: sbstjn.com/0.0.1" \
-H "X-Bunq-Language: en_US" \
-H "X-Bunq-Region: de_DE" \
--data "{\"client_public_key\":\"$KEY\"}"
{
"Response": [
{ "Id": { "id": 234567891 } },
{
"Token": {
"id": 123456789,
"created": "2023-12-09 10:00:00.000000",
"updated": "2023-12-09 10:00:00.000000",
"token": "yXjmuT5sJP51GNN7CBqKebRDOymq6BUYKJefqitK"
}
},
{
"ServerPublicKey": {
"server_public_key": -----BEGIN PUBLIC KEY-----\nMII2323NzaC1yc2EAAAADAQABAAAAQQCo9+BpMRYQ\n6JPTW86CcXrhQ4YATF+5OyTebIx5lBYm8S+/Z\nkCuSXW1sgWxSyKPJo8ejNLlhxjcGaIaKHOysH8ooj4Z\n3MvnAYPujkoELPyNGfkq9NiKKnyt0IWu3FqvcSKoLZa\/dXz\nbVm3xp22Ft0tluXnyPdzZBi9CdKBA7uMVx0iTu\/1XLa4rt\nB7YZ3qRPH0Zz7gJxmGI31j75+L29juLaZPt6fd7rHX+IBGh1UC97nOdvFmiGVwnf\nlQIDAQAB\n-----END PUBLIC KEY-----\n"
}
}
]
}
The response includes your provided public key as well as the needed token
for further API requests to the bunq banking service.
Create bunq API Key
After you sent your public key to bunq, you can head over to the mobile application and create a new API key within the Developers section of your Account Settings.
Next, use the generated API key from the bunq mobile banking application together with the token
response from the initial API request to register your local computer as an allowed Device Server to access bunq.
# Set environment variable for API key from mobile application
$ > export API_KEY=uuFCDtBKKsRWy11VJQF8kQvmTGRmyqrJ03cK7jgG
# Set environment variable for Request token
$ > export TOKEN=yXjmuT5sJP51GNN7CBqKebRDOymq6BUYKJefqitK
# Store payload for request in local file
$ > export PAYLOAD="{\"description\": \"sbstjn.com\", \"secret\": \"$API_KEY\", \"permitted_ips\": [\"*\"]}"
$ > echo -n $PAYLOAD > payload_device_server.json
# Generate signature for request payload
$ > openssl dgst -sha256 -sign bunq.pem -out payload_device_server.sha256 payload_device_server.json
$ > openssl enc -base64 -in payload_device_server.sha256 -out payload_device_server.sha256.base64
# Load signature as single line string to variable
$ > export SIGNATURE=`tr -d '\n' < payload_device_server.sha256.base64`
$ > curl -X POST https://api.bunq.com/v1/device-server \
-H "Content-Type: application/json" \
-H "Cache-Control: no-cache" \
-H "User-Agent: sbstjn.com/0.0.1" \
-H "X-Bunq-Language: en_US" \
-H "X-Bunq-Region: de_DE" \
-H "X-Bunq-Client-Authentication: $TOKEN" \
-H "X-Bunq-Client-Signature: $SIGNATURE" \
--data $PAYLOAD
{
"Response": [
{ "Id": { "id": 123456789 } }
]
}
When switching back to the bunq mobile application, you see the API key is assigned to your application name now; it can no longer accept other incoming requests.
Create bunq Session
Based on the previous steps, you can now use the bunq mobile banking API to create a new user session for your bunq account. Same as for the /device-server
request, use the initially created token of the /installation
response together with your API key from the mobile application to create a new session.
# Set environment variable for API key from mobile application
$ > export API_KEY=uuFCDtBKKsRWy11VJQF8kQvmTGRmyqrJ03cK7jgG
# Set environment variable for Request token
$ > export TOKEN=yXjmuT5sJP51GNN7CBqKebRDOymq6BUYKJefqitK
$ > export PAYLOAD="{\"secret\": \"$API_KEY\"}"
$ > echo -n $PAYLOAD > payload_session_server.json
# Generate signature for request payload
$ > openssl dgst -sha256 -sign bunq.pem -out payload_session_server.sha256 payload_session_server.json
$ > openssl enc -base64 -in payload_session_server.sha256 -out payload_session_server.sha256.base64
$ > export SIGNATURE=`tr -d '\n' < payload_session_server.sha256.base64`
# Load signature as single line string to variable
$ > curl -X POST https://api.bunq.com/v1/session-server \
-H "Content-Type: application/json" \
-H "Cache-Control: no-cache" \
-H "User-Agent: sbstjn.com/0.0.1" \
-H "X-Bunq-Language: en_US" \
-H "X-Bunq-Region: de_DE" \
-H "X-Bunq-Client-Authentication: $TOKEN" \
-H "X-Bunq-Client-Signature: $SIGNATURE" \
--data $PAYLOAD
{
"Response": [
{ "Id": { "id": 123456789 } },
{
"Token": {
"id": 234567891,
"created": "2023-12-09 10:00:00.000123",
"updated": "2023-12-09 10:00:00.000123",
"token": "NLaPy51QN7LwVr4KXtOAfY2pjIf5GGPTYspmrnP6"
}
},
{
"UserPerson": {
"id": 1122334,
"created": "2021-10-15 10:00:00.000123",
"updated": "2023-11-25 10:00:00.000123",
"status": "ACTIVE",
"sub_status": "NONE",
"public_uuid": "c10ee090-0ad3-4ca6-a14b-a2663a08f67f",
"display_name": "Sebastian Müller",
"public_nick_name": "Sebastian",
"language": "en_US",
"region": "de_DE",
"session_timeout": 3600,
"daily_limit_without_confirmation_login": { … },
"alias": [ … ],
"avatar": { … },
"relations": [],
"tax_resident": null,
"notification_filters": [ … ],
"address_main": { … },
"address_postal": { … },
"first_name": "Sebastian",
"middle_name": "",
"last_name": "Müller",
"legal_name": "Sebastian Müller",
"date_of_birth": "XXXX-YY-ZZ",
"place_of_birth": "Example",
"country_of_birth": "DE",
"nationality": "DE",
"gender": "MALE",
"version_terms_of_service": "1",
"deny_reason": null,
"document_issuing_authority": null,
"document_expiry_date": null,
"document_status": "ACTIVE",
"is_primary_document": true,
"customer": { … },
"customer_limit": { … },
"billing_contract": [ … ],
"pack_membership": null,
"premium_trial": null
}
}
]
}
Take note of that values for Token > token
and Response > UserPerson > id
. This is your internal User ID and is required for accessing the monetary accounts for which you want to configure the bunq Callback URL. The token is needed for further API requests.
Available bunq Accounts
Next, you can retrieve the available monetary accounts from your bunq account. Use the token
value from the previous API response and send a basic GET
request:
# Set environment variable for Request token
$ > export TOKEN=NLaPy51QN7LwVr4KXtOAfY2pjIf5GGPTYspmrnP6
# Load signature as single line string to variable
$ > curl -X GET https://api.bunq.com/v1/user/1122334/monetary-account \
-H "Content-Type: application/json" \
-H "Cache-Control: no-cache" \
-H "User-Agent: sbstjn.com/0.0.1" \
-H "X-Bunq-Language: en_US" \
-H "X-Bunq-Region: de_DE" \
-H "X-Bunq-Client-Authentication: $TOKEN"
{
"Response": [
{
"MonetaryAccountBank": {
"id": 6677889,
"created": "2023-11-22 10:00:00.000123",
"updated": "2023-11-22 10:00:00.000123",
"alias": [ … ],
"avatar": {
"uuid": "f836f673-be3a-4bdb-b42b-5bbb461bfbf1",
"image": [ … ],
"anchor_uuid": "c908786b-b796-48e3-82d5-f7eca88f0162",
"style": "NONE"
},
"balance": {
"currency": "EUR",
"value": "9.99"
},
"country": "NL",
"currency": "EUR",
"display_name": "Sebastian M\u00fcller",
"daily_limit": {
"currency": "EUR",
"value": "100.00"
},
"description": "Default",
"public_uuid": "56b2e6ca-4ca3-4504-bb32-3c9dd50d89b0",
"status": "ACTIVE",
"sub_status": "NONE",
"timezone": "europe\/amsterdam",
"user_id": 1122334,
"monetary_account_profile": {
"profile_fill": { … },
"profile_drain": null,
"profile_action_required": "FILL_ACCOUNT_NEEDED",
"profile_amount_required": {
"currency": "EUR",
"value": "50.00"
}
},
"setting": { … },
"connected_cards": [],
"budget": null,
"overdraft_limit": {
"currency": "EUR",
"value": "0.00"
},
"all_auto_save_id": [],
"total_request_pending": {
"currency": "EUR",
"value": "0.00"
}
}
},
{
"MonetaryAccountBank": {
"id": 3344556,
"created": "2023-12-01 10:00:00.000123",
"updated": "2023-12-01 10:00:00.000123",
"alias": [
{
"type": "IBAN",
"value": "NL57ABNA6995898621",
"name": "Sebastian M\u00fcller"
}
],
"avatar": {
"uuid": "28b9fefc-c9e2-4b4a-a3db-b84a96957c78",
"image": [ … ],
"anchor_uuid": "06e7d07a-edfb-4f43-9414-33793497eea7",
"style": "NONE"
},
"balance": {
"currency": "EUR",
"value": "20.00"
},
"country": "NL",
"currency": "EUR",
"display_name": "Sebastian M\u00fcller",
"daily_limit": {
"currency": "EUR",
"value": "50.00"
},
"description": "Development",
"public_uuid": "e556323b-9577-4aca-839b-d498ef54d08d",
"status": "ACTIVE",
"sub_status": "NONE",
"timezone": "europe\/amsterdam",
"user_id": 1122334,
"monetary_account_profile": { … },
"setting": { … },
"connected_cards": [],
"budget": { … },
"overdraft_limit": {
"currency": "EUR",
"value": "0.00"
},
"all_auto_save_id": [],
"total_request_pending": {
"currency": "EUR",
"value": "0.00"
}
}
}
],
"Pagination": {
"future_url": null,
"newer_url": null,
"older_url": null
}
}
The bunq API response includes an object for every one of your monetary accounts. You can configure a custom webhook (also named Callback in bunq) for each of your accounts individually.
Create a file notifications.json
with two Callback endpoint; one for PAYMENT
and one for MUTATION
events:
{
"notification_filters": [
{
"category": "PAYMENT",
"notification_target": "https://example.com/incoming/bunq"
},
{
"category": "MUTATION",
"notification_target": "https://example.com/incoming/bunq"
}
]
}
The listed response of the available bunq monetary accounts included two accounts: 3344556
and 6677889
. To configure the intended Callbacks, send the notifications.json
payload to both API endpoints:
# Generate signature for request payload
$ > openssl dgst -sha256 -sign bunq.pem -out notifications.sha256 notifications.json
$ > openssl enc -base64 -in notifications.sha256 -out notifications.sha256.base64
$ > export SIGNATURE=`tr -d '\n' < notifications.sha256.base64`
# Load signature as single line string to variable
$ > curl -X POST https://api.bunq.com/v1/user/1122334/monetary-account/3344556/notification-filter-url \
-H "Content-Type: application/json" \
-H "Cache-Control: no-cache" \
-H "User-Agent: sbstjn.com/0.0.1" \
-H "X-Bunq-Language: en_US" \
-H "X-Bunq-Region: de_DE" \
-H "X-Bunq-Client-Authentication: $TOKEN" \
-H "X-Bunq-Client-Signature: $SIGNATURE" \
--data @notifications.json
$ > curl -X POST https://api.bunq.com/v1/user/1122334/monetary-account/6677889/notification-filter-url \
-H "Content-Type: application/json" \
-H "Cache-Control: no-cache" \
-H "User-Agent: sbstjn.com/0.0.1" \
-H "X-Bunq-Language: en_US" \
-H "X-Bunq-Region: de_DE" \
-H "X-Bunq-Client-Authentication: $TOKEN" \
-H "X-Bunq-Client-Signature: $SIGNATURE" \
--data @notifications.json
Wow, that’s it! 🎉 Now you will get a nice JSON object whenever something change the balance on your bunq accounts, and for incoming or outgoing payments.
Next, we’re sending this to AWS EventBridge for processing 😍