Open an account

Open a v2 account with one POST /v2/accounts, and read the errors it can return.

API version: v2.

POST /v2/accounts opens an account at the option you chose, with the data its requirements form asks for. There is no draft, submit or process step. The one call starts the bank's review.

A money example. Ada's account for her 1,200.00 USD invoice is a wallet-backed USD account on polygon. You send one request with her details, her passport and a utility bill. The response has status pending. A few minutes or days later the bank approves the account, and her client can pay.

Before you start

  • The customer's basic KYC is completed. The API checks this first. Until it passes, the API reports no other error.
  • You have an option from capabilities and its form from requirements.
  • The customer has not reached max_accounts_per_customer at that option. To count the customer's accounts, use GET /v2/accounts?customer_id=….

Send the request

curl --request POST \
     --url https://api.gravv.xyz/v2/accounts \
     --header 'Api-Key: <API_KEY>' \
     --header 'Idempotency-Key: 5eedf850-57d4-4d57-ad69-6f0593c45c2b' \
     --header 'Content-Type: application/json' \
     --data '
{
  "customer_id": "0b6f3c1e-2a4d-4c8e-9f10-7d2a6b5e4c33",
  "customer_type": "individual",
  "currency": "USD",
  "account_type": "operating",
  "category": "sub_account",
  "bank_code": "<bank_code>",
  "network": "polygon",
  "label": "Ada USD",
  "data": {
    "root": {
      "first_name": "Ada",
      "last_name": "Okafor",
      "date_of_birth": "1991-03-14",
      "email": "ada.okafor@example.com",
      "phone_daytime": "+447911123456",
      "gender": "female",
      "citizenship_country": "GB",
      "tax_residence_country": "GB",
      "permanent_address": {
        "address_line": "12 Crown Street",
        "city": "London",
        "state": "GB-LND",
        "postal_code": "EC1A 1BB",
        "country": "GB"
      }
    },
    "documents": [
      {
        "document_type": "passport",
        "issuing_country": "GB",
        "number": "533401372",
        "issued_date": "2022-01-31",
        "expiration_date": "2032-01-30",
        "filename": "passport.jpg",
        "content": "<base64 of the passport photo page>"
      },
      {
        "document_type": "proof_of_address",
        "issuing_country": "GB",
        "filename": "utility-bill.pdf",
        "content": "<base64 of a recent utility bill>"
      }
    ]
  }
}
'
{
  "data": {
    "account_id": "6f1c2a9e-3b1d-4f7a-9c62-0d8e4b7a5c11",
    "status": "pending"
  },
  "error": null
}

Save account_id. The response has no application_id, so read it from GET /v2/accounts/{id}.

What to send

  • For a wallet-backed option (non-empty token_wallets), send network from the option's token_options. Instead of network, you can send the wallet_id of one of your wallets on one of those networks. Never send both.
  • For a fiat-only option, send neither network nor wallet_id.
  • For a customer's account, send category sub_account. For NGN, always send category.
  • Send customer_type. In data.root, send citizenship_country for a person or incorporation_country for a business.
  • For a business, send data.parties[]. Name each person from the customer record by id, and add the facts the record lacks (ownership, control, signer, documents). If you leave parties out, the API declares every associated person without those facts, and the request fails validation on them.
  • Identity documents carry number, issued_date, expiration_date and issuing_country. content is raw base64, not a data: URI.
  • Each document is at most 2 MB. In sandbox, keep the files in one request under 500 KB in total. Sandbox refuses requests from about 600 KB with a 413.
  • Gravv does not deduplicate a request body over 1 MiB under its Idempotency-Key. Documents of about 770 KB or more make a body that large. A retry of such a request runs again instead of returning the first response. After a timeout, list the customer's accounts before you send it again.

Errors

Every 400 lists all the problems it found in details, keyed by field. Fix them all, then send the request again with a new Idempotency-Key. Gravv stores a 4xx response under its key for 24 hours and replays it if you reuse the key.

Response Why Fix
400 the customer for the customer_id supplied has not completed KYC; … (CUSTOMER_KYC_INCOMPLETE) basic KYC is not completed Finish KYC, then send again with a new key
400 no customer record exists for the customer_id supplied; … (CUSTOMER_NOT_FOUND) Wrong id, or an id from the other environment Use the id from POST /v1/customers in the same environment
400 Date of Birth is required (and 1 other field; see details) Missing fields, each named in details Fill every key in details
400 network is required for account options that support wallet integration Wallet-backed option without network or wallet_id Send network
400 network is only accepted for account options that support wallet integration network sent to a fiat-only option Remove it
400 this account does not provide a token wallet on bitcoin; … (SELECTION_CONFLICT) The option does not offer that network Use one of its token_options
400 Network must be sent as a top-level request field; data.root.network is no longer accepted network inside data.root Move it to the top level
400 invalid request payload: unknown field "blockchain_network" An old field name Send network
400 wallet_id and network are mutually exclusive; send only one Both sent Send one
400 bank_code=<code> does not open accounts for customers resident in XX. … (COUNTRY_NOT_SERVED) The option does not serve that country Choose another option
400 bank_code=<code> does not offer currency=GBP; … (CURRENCY_NOT_SUPPORTED) The option does not offer that currency Choose another option
400 details.documents: required: requires document proof_of_address A required document is missing Add it to data.documents
400 document content too large: maximum size is 2MB; … A file over 2 MB Compress it
400 unsupported file type "image/heic"; allowed types are JPEG, PNG, and PDF Wrong format Convert it
400 content must be base64 (standard or URL-safe) A data: prefix or bad base64 Send raw base64
400 details.gender: invalid_enum: … expected one of: female, male These forms do not accept other Ask the customer, or choose another option
400 state "LND" is not an ISO-3166-2 subdivision; … A bare state code Send the full code, such as GB-LND or US-CA
400 parties are not accepted for an individual customer; … parties on an individual Remove parties
400 customer_type=business does not match this customer's registered profile, which is individual. … Wrong customer_type Send the customer's type
400 this customer already has an existing reference at the resolved bank; root-level fields are no longer accepted for it … The bank already knows this customer Send no data.root
409 this customer already holds the maximum number of USD accounts permitted for this account type (5) The per-customer limit Reuse an account, or ask support to raise the limit
409 customer already has an application in progress for this bank An earlier application at this bank is open Fix that application
409 this account provider is currently disabled for this account The option is switched off for your business or this customer Choose another option, or contact support
422 idempotency_key_reused The same key with a different body Use a new key for each different request
503 Service temporarily unavailable. …, 500, 504 A temporary fault Retry with the same key. Gravv does not open a second account

Webhooks

Gravv sends applications.accounts.pending, then possibly applications.accounts.blocked, then applications.accounts.completed or applications.accounts.failed. Gravv never sends account.activated for a v2 account you open. See Track activation.

Was this page helpful?