SEPA Payments Quickstart
Get started with the Circle Payments API and accept your first SEPA payment that settles in USDC!
The Circle Payments API allows you to receive SEPA payments in 35+ countries that settle into USDC.
1. Get an API key
The Circle Payments API uses API keys as the mechanism to authenticate client requests. The API key must be set in the Authorization
header of the request sent from your backend server. The format of the header is Bearer secret-key-value
.
Learn more about authenticating API calls here.
To obtain an API key for the sandbox environment, simply create an account and generate a new key in settings - it only takes a few seconds.
Once you have generated your API key, record it in a secure place.
2. Install the sample application
We have built a sample web application to demonstrate the use of Circle Payments API. You can clone the Github repository, configure the app and run it locally.
NPM and Yarn
If you don't already, make sure you have npm and yarn installed.
# Clone the sample app Github repository
git clone https://github.com/circlefin/payments-sample-app.git
# Change into sample app folder
cd payments-sample-app
# Create a .env file and configure the base url for api calls
echo BASE_URL=https://api-sandbox.circle.com > .env
# Install the dependencies
yarn install
# Run the sample app locally
yarn dev
Enter Your API Key
Take the API key you generated on step 1, and enter it on the settings tab on the top right corner of the sample application.
3. Create the Bank Account You Will Accept a Payment From
Now make an API call to create the bank account you intend to accept a payment from by using the create bank account for SEPA transfers endpoint.
You can do that by using the "Payments API" > "POST /sepa" flow on the sample application menu. You can select one of three pre-existent test bank account details. Alternatively, you can use the command below to make an API call yourself.
# Replace ${YOUR_API_KEY} with the API key obtained on step 1
curl -H 'Accept: application/json' \
-H 'Content-type: application/json' \
-H "Authorization: Bearer ${YOUR_API_KEY}" \
-X POST --url https://api-sandbox.circle.com/v1/banks/sepa \
--data '
{
"idempotencyKey": "6ae62bf2-bd71-49ce-a599-165ffcc33680",
"iban": "IE31100400480532013000",
"billingDetails": {
"name": "Satoshi Nakamoto",
"city": "Dublin",
"country": "IE",
"line1": "1 Dublin Street",
"postalCode": "D01 AY67"
}
}
'
In either case, you should receive a response like below.
{
"data": {
"id": "a6ba73a8-cc77-46c5-bbbc-ce7a97ecb372",
"status": "pending",
"description": "Bank of Ireland ****0010",
"trackingRef": "CIR13FB13A",
"fingerprint": "eb170539-9e1c-4e92-bf4f-1d09534fdca2",
"billingDetails": {
"name": "Satoshi Nakamoto",
"city": "Dublin",
"country": "IE",
"line1": "1 Dublin Street",
"postalCode": "D01 AY67"
},
"createDate": "2020-04-10T02:13:30.000Z",
"updateDate": "2020-04-10T02:13:30.000Z"
}
}
4. Obtain the Bank SEPA Transfer Details
Circle SEPA transfers are currently only "push" payments, and so the end user making the payment has to initiate the transfer from their bank account (see our list of supported countries). For that reason it is important to be able to share bank SEPA transfer details with the end user.
You can obtain the details required for initiating the bank SEPA transfer by using the get SEPA transfer instructions endpoint. Do that by using the "Payments API" > "GET /sepa/{id}/instructions" flow on the sample application menu. Alternatively, you can use the command below to make an API call yourself. Use the id
of the bank account created in the previous step.
curl -H 'Accept: application/json' \
-H "Authorization: Bearer ${YOUR_API_KEY}" \
-X GET --url https://api-sandbox.circle.com/v1/banks/sepa/${BANK_ACCOUNT_ID}/instructions
In either case, you should receive a response like below.
{
"data": {
"trackingRef": "CIR3ZN75QG",
"beneficiary": {
"name": "CIRCLE LLC",
"address1": "99 HIGH STREET",
"address2": "LEVEL 17 SUITE 1701"
},
"beneficiaryBank": {
"name": "LHV Pank",
"address": "Tartu mnt 2, 10145 Tallinn",
"postalCode": "92037",
"country": "EE",
"bic": "LHVBEE22XXX",
"iban": "EE691269412446551399"
}
}
}
In the production environment, the transfer details above will represent Circle's bank account details for incoming transfers. That information should be shared with the end user initiating the bank SEPA transfer. Once the funds reach Circle's banking infrastructure, we will attempt to reconcile that payment and settle the funds on your account.
Bank SEPA Transfer Instructions
It is very important that you build an experience on your product that ensures end users will utilize the correct bank SEPA transfer details when making a payment. Mistakes made on a bank SEPA transfer can cause payment delays and ultimately cause the payment to fail along the way.
Transfer Tracking Reference
Note that a bank account created via Circle APIs has a tracking reference (
trackingRef
attribute) that is unique to that bank account. For payments via bank SEPA transfer, it is imperative that the end user initiating the transfer include that value on thepayment reference
field when prompted for it by their bank. Failure to indicate the tracking reference makes it impossible for Circle to reconcile the payment - such transfers will be returned to the sender.
5. Accept a Payment Via Bank SEPA Transfer
Now that you have obtained bank SEPA transfer details for the payment and shared those with the end user, you just need to wait for the transfer to be initiated by the end user, and eventually the payment to settle and be reconciled.
It is difficult to test flows involving push payments, so we have created an endpoint for creating a mock SEPA transfer. You can go to "Payments API" > "POST /mocks/payments/sepa" on the sample application menu to utilize it. Alternatively, you can use the command below to make an API call yourself. Use the trackingRef
obtained in the previous step.
curl -H 'Accept: application/json' \
-H 'Content-type: application/json' \
-H "Authorization: Bearer ${YOUR_API_KEY}" \
-X POST --url https://api-sandbox.circle.com/v1/mocks/payments/sepa \
--data '
{
"trackingRef": "${TRACKING_REF}",
"amount": {
"amount": "1000.00",
"currency": "EUR"
}
}
'
In either case, you should receive a response like below.
{
"data":{
"trackingRef":"CIR2JYD6WT",
"amount":{
"amount":"1000.00",
"currency":"EUR"
},
"status":"pending"
}
}
6. Check for the Existence of Payment
Due to "push" nature of SEPA transfers, you will have to monitor payments into your account in order to detect the SEPA payment you are expecting. You can either poll for payments using the get payments endpoint at certain intervals, or use our notifications service to receive updates.
Eventually, you will observe a payment object that matches your expectation, such as the one below.
Automated FX
SEPA payments are denominated in EUR, and Circle settles the SEPA payments in USDC, therefore the transactions will be automatically converted. The payment response objects also communicates the original EUR amounts in "fromAmount" field.
{
"data": {
"id": "c82a2f41-5ec5-4d86-9395-fcdf0d0da552",
"type": "payment",
"merchantId": "7cbdedb1-d526-46b4-af12-4162a002eb9c",
"merchantWalletId": "1000005019",
"source": {
"id": "cc3ac8e5-4dd7-4061-b465-0d2e7947ce4c",
"type": "sepa"
},
"description": "Merchant push payment",
"fromAmount": {
"amount": "1000.00",
"currency": "EUR"
},
"amount": {
"amount": "1050.00",
"currency": "USD"
},
"fees": {
"amount": "0.20",
"currency": "USD"
},
"status": "paid",
"refunds": [],
"createDate": "2020-10-15T11:20:01.346Z",
"updateDate": "2020-10-15T11:28:06.117Z"
}
}
You can detect a match by looking into the source.id
attribute, as it should match the id
of the bank account you created on step 3.
When the payment is settled, the status
will be equals to paid
.
If the status
is equal to failed
then it means we could not settle that payment. That typically happens due to a mismatch between the information provided during the creation of the bank account (step 3) and the information received from the sender bank.
In the case when a SEPA payment has failed, the funds are automatically sent back to the sender.
- If FX has taken place, fees will be assessed.
- If FX has not taken place, fees will not be assessed and the payment object will represent data.amount.currency in EUR.
{
"data": {
"id": "c82a2f41-5ec5-4d86-9395-fcdf0d0da552",
"type": "payment",
"merchantId": "7cbdedb1-d526-46b4-af12-4162a002eb9c",
"merchantWalletId": "1000005019",
"source": {
"id": "cc3ac8e5-4dd7-4061-b465-0d2e7947ce4c",
"type": "sepa"
},
"description": "Merchant push payment",
"amount": {
"amount": "1000.00",
"currency": "EUR"
},
"fees": {
"amount": "0.00",
"currency": "USD"
},
"status": "failed",
"refunds": [],
"createDate": "2020-10-15T11:20:01.346Z",
"updateDate": "2020-10-15T11:28:06.117Z"
}
}
Bank Account Verification
Due to regulatory obligations, our systems automatically compare the bank account (iban) and sender (first and last name) information provided at the time of bank account creation with the information received on the SEPA transfer details (what the sender bank transmits). When there are mismatches, we are obliged to fail the payment and return the funds.
To avoid returns, make sure you build a user experience that makes that clear for end users, so that they provide you with the correct bank account and account holder / beneficiary information.
🎉 You are all done!
7. Like what you see? Keep going!
By accepting a SEPA payment that settles in USDC, you have just touched the tip of the iceberg of Circle APIs.
Check how to subscribe to notifications on changes in payments status. Also make sure you check the rest of the documentation - a few starting points are suggested below.
8. Ready for the next step?
If you are in advanced stages of experimenting with our APIs and want to plan moving to production, please start by applying for a Circle Account and subsequently reach out to sales. We'll be happy to walk you through to the next steps.
We can't wait to see what you are going to build!
Updated 3 months ago