Where to read them
They live on the account object.GET /v1/accounts/{account_id} returns the requirements block on every read.
All take scope
connected_accounts:read.
entries[] is the one to build a form from when an account holds more than one capability: a flat bucket cannot say that one field blocks payouts while another blocks nothing the account has asked for.
Field states
Each entry inrequirements.entries[] carries a status, naming the bucket it is listed under.
Only outstanding fields become entries. A field that has been provided and accepted is not listed at all, so an empty
entries means nothing is outstanding. A field awaiting a reviewer is reported as pending_verification rather than a status of its own: to a platform, both mean the same thing, which is that the field is with us and re-sending it achieves nothing.
provided says whether a value was submitted. A currently_due field with provided: true was rejected; with provided: false it was never filled in. A rejected field carries error_reason, written for display to the account holder.
The field key names what the entry is about. company.* and business_profile.* are the account itself, persons.per_3a91c0d7.* a specific person. Read that person at /persons/{person_id}.
What a field blocks
Each entry carriesrestricts_capabilities, the capabilities that stay off while it is outstanding. An empty array means no capability the account has asked for needs this field.
Read it per entry rather than counting the buckets: an account holding two capabilities can have one fully satisfied while the other is blocked by a single field, which a flat list cannot express.
Who can act, and by when
Each entry carries aresolution.
A field with a deadline carries it as
deadline, and the account’s requirements.current_deadline is the soonest one across everything outstanding, enough to drive a banner without walking every entry. Both are null when no deadline has been set.
The account roll-up
The account object carries a smaller block for a status badge:errors carries a field that was provided and then rejected, as distinct from one that is missing.
Submitting
POST /v1/accounts/{account_id} · scope connected_accounts:write
fields is keyed by the field keys the requirements name: persons, company.*, business_profile.*, payout_destination. A submission that omits a field leaves it untouched, so you can save an account holder’s progress as they fill in a form. But every field you do send is validated together, and if any one of them is invalid the submission is refused: none of the fields in that call are saved, not even the ones that were fine. There is no partial save of a rejected submission. Retry with only the corrected fields, or the whole fields object again once it is fixed.
This applies to every field class in the payload, not only payout_destination: an invalid company.structure or a malformed entry in persons[] in the same call also refuses the whole submission.
A field that is merely incomplete (one you have started but not finished, so a required sub-field is still absent) is not a rejection. The call succeeds and the requirement stays in currently_due until you send the rest. You still see it in errors[] with the code required, alongside any field that was actually rejected.
The refusal covers the fields object, not the rest of the request. Contact details, capability requests, profile changes and an entity-type change sent in the same call are applied before the fields are validated, and a rejected submission does not undo them. A call that mixes them is not atomic: if the fields are refused, retry the fields; the rest already took effect.
Requirement values ride on the account write, so the same call can set contact details and request capabilities. See Update an account.
The response is the account, with its recomputed requirements block.
The persons shape
persons is an array. Each entry is one human, identified across calls by the persons.<person_id> prefix its requirements carry once created. Submit a person by including it in the array; omit a field to leave it untouched, so a form can save as it is filled.
persons.<id>.name, .dob, .id_number, .id_document, …) come from the capabilities the account holds: a recipient needs only a name, a merchant needs the full identity set. The id_number requirement is satisfied by any non-BVN entry in id_numbers. persons.bvn and persons.proof_of_address are eventually_due and do not block getting started; submit the bvn when asked by adding it to the list.
The business_profile shape
A merchant (an account that accepts payments) describes its business. These fields gate the payment-method capabilities (card_collection, ngn_card_collection, bank_transfer, …); a recipient does not owe them.
Company fields
A registered business (entity_type: "company") also owes company.* fields — registration number, structure, and country-specific filings. company.structure is an enum whose valid values, and which documents each structure owes, depend on the country; read them from GET /v1/reference/business-structures. An individual account (entity_type: "individual") owes none of these.
Documents (id_document, company filings)
A document requirement (a person’s id_document, a company’s certificate of incorporation) is a file, not a value, so it is uploaded separately and then referenced. It cannot be sent inline in the fields object.
1
Upload the file
POST /v1/utilities/uploads (multipart), with the bytes and a scope that labels the upload:identity_document— a person’s government ID.account_requirement— a company filing or supporting evidence.
upload_id. Accepted formats are the common image and PDF types; a PDF identity document is fine.2
Attach it
Attach the uploaded file to the slot it satisfies, referencing the
upload_id from the previous step. A person’s identity document attaches to that person: POST /v1/accounts/{account_id}/persons/{person_id}/documents with the upload_id and the document slot. See Verify an account’s identity. A company filing attaches to the account: POST /v1/accounts/{account_id}/documents, naming the company document slot.POST /v1/utilities/uploads.
The payout_destination shape
payout_destination is a tagged union: type picks the rail, and the rest of the object is whichever fields that rail needs.
currency is required alongside them (a legacy submission that omits it is inferred from the account’s country instead). A currency with no configured payout provider is rejected outright, whatever shape its object is otherwise in. Today that means:
NGNto abank_accountis payable.USDT_TRC20andUSDT_BEP20to acrypto_walletare payable.- Nothing else is. In particular,
mobile_moneyis a shape this field understands (it will check yourphone_numberandmobile_provider), but no payout provider is currently configured for any mobile money currency, so everymobile_moneysubmission is rejected oncurrency. Do not build against GHS or KES mobile money payouts; they are not live.
bank_code and a mobile money submission’s mobile_provider are also checked against the account’s own country: a Ghanaian account cannot name a Nigerian bank, and vice versa.
When a submission is rejected
A rejected write returns400 with error_code: "INVALID_REQUIREMENT_FIELD" and an errors array, one entry per rejected field:
See Errors for the error envelope and Error Reference for every
error_code.

