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.

  1. The agency sends 2,000.00 EUR by SEPA to Lucas's IBAN.
  2. The bank converts the euros to USDC and delivers them to the account's wallet on polygon.
  3. Gravv sends transfer.inflow.deposit_settled with the booked amount and currency.

Before you start

  • The customer's basic KYC is completed.
  • Capabilities with currency=EUR (or GBP) list an option with a non-empty token_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. 0 is a valid count.
  • Anyone who is not a US citizen must answer immigration_status (permanent_resident, non_permanent_resident or non_resident) and the four international_supplement questions.

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).

Reference

Was this page helpful?