Read account requirements

Use GET /v2/accounts/requirements to get the fields and documents a bank needs, and turn the form into a create request.

API version: v2.

Each bank asks for different data. GET /v2/accounts/requirements returns the form for one option. The form lists the fields, the documents and, for a business, the people (parties) to declare. Read it before you open the account, so the create call succeeds the first time.

A money example. Ada's 1,200.00 USD invoice needs a USD account. The default USD option for a GB individual asks for 13 fields (names, date of birth, email, phone, gender, citizenship, tax residence and a full address) and 2 documents (an identity document and a proof of address). You collect those once, and the account opens without a second round.

Ask for the form

Always send customer_type, currency, bank_code and country. The create call picks the bank from the country inside your data, while this call picks it from the country parameter. Unless you send both bank_code and country, you can get the form of a different bank. Send customer_id too, to see what is already on file:

curl --request GET \
     --url 'https://api.gravv.xyz/v2/accounts/requirements?customer_type=individual&currency=USD&bank_code=<bank_code>&country=GB&customer_id=0b6f3c1e-2a4d-4c8e-9f10-7d2a6b5e4c33' \
     --header 'Api-Key: <API_KEY>'

Here is the response, trimmed to 3 fields and 2 suggestions (checked in sandbox):

{
  "data": {
    "documents": [
      {"new_document_fields": ["document_type", "content", "issuing_country", "number", "issued_date", "expiration_date"], "required": true, "scope": "root", "types": ["national_id", "passport", "drivers_license", "permanent_resident_card", "employment_authorization", "other"]},
      {"new_document_fields": ["document_type", "content", "issuing_country"], "required": true, "scope": "root", "types": ["proof_of_address"]}
    ],
    "fields": [
      {"group": "identity", "is_predefined": true, "label": "First Name", "name": "first_name", "required": true, "scope": "root", "type": "string", "write_only": false},
      {"enum": "Gender", "group": "identity", "is_predefined": true, "label": "Gender", "name": "gender", "options": ["female", "male"], "required": true, "scope": "root", "type": "enum", "write_only": false},
      {"applies_when": {"field": "citizenship_country", "op": "eq", "value": "US"}, "group": "us_tax", "is_predefined": false, "label": "Tax ID Number", "name": "tax_id_number", "required": true, "scope": "root", "type": "string", "write_only": true}
    ],
    "has_existing_ref": false,
    "max_parties": null,
    "min_parties": null,
    "suggestions": {
      "documents": {"passport": {"message": "already on file for this customer; upload a new one to override it", "value": "passport"}},
      "fields": {"first_name": {"message": "on file for this customer; send this field to override it", "value": "Ada"}}
    },
    "token_options": [{"asset": "usdc", "network": "polygon"}, {"asset": "usdc", "network": "ethereum"}, {"asset": "usdc", "network": "solana"}]
  },
  "error": null
}

Turn the form into a request

The form says Put it in the create body
a field with scope root data.root.<name>. A dotted name is nested: permanent_address.city becomes "permanent_address": {"city": "…"}
a field with scope party[*] every entry of data.parties[] (directors, owners, signers of a business)
new_party_fields the fields for a person who is not on the customer record (no id)
a document with scope root one entry in data.documents[], of any one of the listed types
a document with scope party[*] that party's own documents[]
new_document_fields the keys a new upload carries: document_type, content (base64; JPEG, PNG or PDF; at most 2 MB, and in sandbox under 500 KB for all of one request's files), issuing_country, and number, issued_date, expiration_date for identity documents
required: true and no condition always send it
applies_when send it only when the condition is true for this customer, for example only for US citizens
options the only accepted values
write_only: true a secret, such as a tax number. The API never returns it
is_predefined: true, or the field is in suggestions.fields already on the customer record. Leave it out and Gravv fills it, or send it to override
suggestions.documents.<type> a document of that type is on file
token_options the networks you can send as network
min_parties, max_parties how many people a business must declare. null means none needed
has_existing_ref: true the bank already knows this customer, so send no data.root
existing_application an application is already open at this bank, so fix that one instead

These parts of the form are not yet live: a document with a required_when condition, a metadata object on a document, a predefined object, and the autofill_tax_form question. They exist only on a build that has not reached production. In production, every document carries required (true, or false when the bank fills a tax form itself). Sandbox already returns the new shape for some forms.

Rules the form does not show

  • Top level, not in data.root. Send customer_id, customer_type, currency, account_type, category, label, bank_code, network and wallet_id at the top level. The API refuses network or wallet_id inside data.root.
  • Always send customer_type and the routing country. Send the routing country in data.root, as citizenship_country for a person or incorporation_country for a business. Gravv chooses the bank from your request before it fills in any missing fields.
  • Filled in for you. If you leave them out, Gravv fills in names, date_of_birth, email, phone, gender, legal_name, registration_number, business_description, business_type and industry. A filled-in citizenship_country or tax_residence_country is the customer's address country, so send them when they differ. Gravv does not fill in the address, so send permanent_address yourself.
  • Documents on file. If you send no documents, Gravv attaches the customer's documents on file. If you send even one, Gravv uses only what you send. To reuse a file, send {"id": "<document id>"} instead of content.
  • Registration type. registration_type defaults from customer_type. Send it only for a joint or minor account.
  • Gender. gender accepts female or male on these forms. If you created the customer with other, the form suggests other but refuses it. Ask the customer which value to send.
  • Old field names. Gravv still maps old names such as dob or ssn. Send the name the form gives. Sending both returns alias_conflict.

Errors

Response Why Fix
400 Customer Type is required (and 1 other field; see details) with details.customer_type and details.currency Both are required Send them
400 no account option is available for the requested combination (…), with UNSUPPORTED_COMBINATION in details No option serves the values you sent. Also returned for an unknown bank_code, or a bank_code that does not serve that country or currency Copy bank_code, currency and country from one option in capabilities
400 invalid request payload: unknown field "limit" Unknown query parameter Send only the documented ones
A business form for an individual customer The requirements call does not check customer_type against the record Send the customer's own type. The create call refuses a mismatch
No suggestions although you sent customer_id The customer lookup failed Retry. The form itself is correct

Next

Open the account with the data you collected.

Was this page helpful?