Open a wallet-backed EUR or GBP account
Open a EUR account with an IBAN or a GBP account with a sort code, for a person or a business, whose deposits arrive as USDC.
Open a EUR account with an IBAN, or a GBP account with a sort code, for a person or a business. Deposits to the account arrive as USDC.
API version: v2.
Who it is for. A cross-border payroll company that pays EU and UK contractors. It wants each contractor's euro or pound income to arrive as USDC that it can pay out on any rail.
Status. EUR is Available on two options. GBP is Available on one. Gravv does not offer either currency in every country, so read capabilities for each customer.
Money example
A German agency pays Lucas, a freelance designer in Lisbon, 2,000.00 EUR.
- The agency sends 2,000.00 EUR by SEPA to Lucas's IBAN.
- The bank converts the euros to USDC and delivers them to the account's wallet on polygon.
- Gravv sends
transfer.inflow.deposit_settledwith the bookedamountandcurrency.
Before you start
- The customer's
basicKYC iscompleted. - Capabilities with
currency=EUR(orGBP) list an option with a non-emptytoken_wallets. For GBP, only the USD, EUR and GBP option qualifies. Its account limit counts USD, EUR and GBP accounts together. - Plan for the customer to open a link. This option often asks them to accept the bank's terms.
Find out what is required
EUR and GBP use the same form as USD for the same option, so the tables in Open a wallet-backed USD account for a person and for a business apply. On the USD, EUR and GBP option, two more rules apply:
- The form asks for
expected_activity.ach_in.*even for EUR and GBP.0is a valid count. - Anyone who is not a US citizen must answer
immigration_status(permanent_resident,non_permanent_residentornon_resident) and the fourinternational_supplementquestions.
The EUR and USD option does not offer GBP.
Steps
1. Open the account
curl --request POST \
--url https://api.gravv.xyz/v2/accounts \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: 2f1ba63b-bfa7-4096-b1f5-d67c262feec1' \
--header 'Content-Type: application/json' \
--data '
{
"customer_id": "7c8d9e0f-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"customer_type": "individual",
"currency": "EUR",
"account_type": "operating",
"category": "sub_account",
"bank_code": "<bank_code>",
"network": "polygon",
"label": "Lucas EUR",
"data": {
"root": {
"first_name": "Lucas",
"last_name": "Ferreira",
"date_of_birth": "1993-08-21",
"email": "lucas.ferreira@example.com",
"citizenship_country": "PT",
"tax_residence_country": "PT",
"immigration_status": "non_resident",
"permanent_address": {"address_line": "Rua Augusta 100", "city": "Lisboa", "state": "PT-11", "postal_code": "1100-053", "country": "PT"},
"international_supplement": {"has_us_bank_account": false, "has_denied_bank_account": false, "has_regulatory_action": false, "is_politically_exposed": false},
"expected_activity": {"expected_avg_daily_balance": 3000, "ach_in": {"monthly_count": 0, "monthly_amount": 0}}
},
"documents": [
{"document_type": "national_id", "document_type_side": "front", "issuing_country": "PT", "number": "123456789ZZ1", "issued_date": "2021-02-01", "expiration_date": "2031-02-01", "filename": "id-front.jpg", "content": "<base64>"},
{"document_type": "proof_of_address", "issuing_country": "PT", "filename": "bill.pdf", "content": "<base64>"}
]
}
}
'
const response = await fetch("https://api.gravv.xyz/v2/accounts", {
method: "POST",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Idempotency-Key": "2f1ba63b-bfa7-4096-b1f5-d67c262feec1",
"Content-Type": "application/json",
},
body: JSON.stringify({
"customer_id": "7c8d9e0f-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"customer_type": "individual",
"currency": "EUR",
"account_type": "operating",
"category": "sub_account",
"bank_code": "<bank_code>",
"network": "polygon",
"label": "Lucas EUR",
"data": {
"root": {
"first_name": "Lucas",
"last_name": "Ferreira",
"date_of_birth": "1993-08-21",
"email": "lucas.ferreira@example.com",
"citizenship_country": "PT",
"tax_residence_country": "PT",
"immigration_status": "non_resident",
"permanent_address": {
"address_line": "Rua Augusta 100",
"city": "Lisboa",
"state": "PT-11",
"postal_code": "1100-053",
"country": "PT"
},
"international_supplement": {
"has_us_bank_account": false,
"has_denied_bank_account": false,
"has_regulatory_action": false,
"is_politically_exposed": false
},
"expected_activity": {
"expected_avg_daily_balance": 3000,
"ach_in": {
"monthly_count": 0,
"monthly_amount": 0
}
}
},
"documents": [
{
"document_type": "national_id",
"document_type_side": "front",
"issuing_country": "PT",
"number": "123456789ZZ1",
"issued_date": "2021-02-01",
"expiration_date": "2031-02-01",
"filename": "id-front.jpg",
"content": "<base64>"
},
{
"document_type": "proof_of_address",
"issuing_country": "PT",
"filename": "bill.pdf",
"content": "<base64>"
}
]
}
}),
});
console.log(response.status, await response.json());
import os
import requests
response = requests.post(
"https://api.gravv.xyz/v2/accounts",
headers={
"Api-Key": os.environ["GRAVV_API_KEY"],
"Idempotency-Key": "2f1ba63b-bfa7-4096-b1f5-d67c262feec1",
"Content-Type": "application/json",
},
json={
"customer_id": "7c8d9e0f-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"customer_type": "individual",
"currency": "EUR",
"account_type": "operating",
"category": "sub_account",
"bank_code": "<bank_code>",
"network": "polygon",
"label": "Lucas EUR",
"data": {
"root": {
"first_name": "Lucas",
"last_name": "Ferreira",
"date_of_birth": "1993-08-21",
"email": "lucas.ferreira@example.com",
"citizenship_country": "PT",
"tax_residence_country": "PT",
"immigration_status": "non_resident",
"permanent_address": {
"address_line": "Rua Augusta 100",
"city": "Lisboa",
"state": "PT-11",
"postal_code": "1100-053",
"country": "PT",
},
"international_supplement": {
"has_us_bank_account": False,
"has_denied_bank_account": False,
"has_regulatory_action": False,
"is_politically_exposed": False,
},
"expected_activity": {
"expected_avg_daily_balance": 3000,
"ach_in": {
"monthly_count": 0,
"monthly_amount": 0,
},
},
},
"documents": [
{
"document_type": "national_id",
"document_type_side": "front",
"issuing_country": "PT",
"number": "123456789ZZ1",
"issued_date": "2021-02-01",
"expiration_date": "2031-02-01",
"filename": "id-front.jpg",
"content": "<base64>",
},
{
"document_type": "proof_of_address",
"issuing_country": "PT",
"filename": "bill.pdf",
"content": "<base64>",
},
],
},
},
)
print(response.status_code, response.json())
The API returns {"account_id": "…", "status": "pending"}. For GBP, send "currency": "GBP" with the same option's bank_code.
2. Send the customer to the terms, if asked
Read GET /v2/accounts/{id}. This option often blocks with status_reason tos_pending. Give the customer required_action_url so that they can accept the bank's terms. The API has no call to accept them. The account resumes on its own after the customer accepts. See Unblock an application.
3. Give the details to the payer
When the account is active, show the IBAN and BIC for EUR, or the account number and sort code for GBP. See Share account details and receive a deposit.
If the customer already has a USD or EUR account at this option, the bank already knows them. For a GBP account, requirements then shows has_existing_ref: true, and you send no data.root on create. See Open a second account.
What can go wrong
Every error in the USD recipes can happen here too. These errors are specific to EUR and GBP:
| Response | Why | Fix |
|---|---|---|
400 bank_code=<code> does not offer currency=GBP; it offers EUR or USD. … (CURRENCY_NOT_SUPPORTED) |
You asked for GBP on the EUR and USD option | Use the USD, EUR and GBP option |
400 details.immigration_status: required: is required |
A non-US citizen did not answer the questions | Answer them |
400 details.expected_activity.ach_in.monthly_count: required: is required |
This option asks for it on every currency | Send the numbers. 0 is valid |
409 this customer already holds the maximum number of USD/EUR/GBP accounts permitted for this account type (5) |
The customer already has five accounts across the three currencies | Open a second account |
400 this customer already has an existing reference at the resolved bank; root-level fields are no longer accepted for it … |
You sent data.root for a second account at this option |
Drop data.root |
status_reason tos_pending and nothing happens |
The customer has not opened the link | Send them to required_action_url |
Webhooks you will receive
Gravv sends applications.accounts.pending, then applications.accounts.blocked with metadata.block_reason tos_pending and metadata.action_url, then applications.accounts.completed. See Account application events.
Testing in sandbox
Both options call their banks' test systems, and neither is instant. Simulate deposits with rail sepa (EUR) or fps (GBP).