Requirements are outstanding actions that must be resolved before a capability becomes active. They appear as a map on the customer object, keyed by field name, with each entry describing the status and which capabilities it affects.
For most customers with straightforward identity verification, requirements resolve automatically in under 60 seconds with no action needed. This page covers the cases where something is outstanding.
Requirements map
requirements is a map<string, Requirement> keyed by field name. The key itself is the requirement type:
"requirements": {
"fullSsn": { "status": "pending", "impact": ["custodyFiat", "custodyStablecoin", "transferFiat"] },
"addressSubdivision": { "status": "due", "impact": ["custodyFiat", "custodyStablecoin", "transferFiat"] },
"tos": { "status": "due", "impact": ["custodyFiat"] }
}
| Field | Description |
|---|
| key | The field name that is required (e.g., fullSsn, addressSubdivision, tos) |
status | Current state. See Requirement statuses below |
deadline | Optional. Deadline for submission (ISO 8601) |
impact | Which capabilities this requirement is blocking |
Requirement statuses
| Status | Meaning |
|---|
eventually_due | Possible future requirement; no action needed yet |
due | Must be submitted (may have a deadline) |
pending | Submitted, awaiting verification |
rejected | Verification failed; resubmit the field |
overdue | Deadline passed without submission |
Requirements in due, rejected, or overdue states need action. Requirements in pending are being processed asynchronously.
Requirement keys and resolution
All field-based requirements resolve by submitting the corresponding field via POST /v2/customers/{customerId}. The only non-field key is tos.
| Requirement key | Resolved by |
|---|
firstName, lastName | Submit individual.firstName, individual.lastName |
dateOfBirth | Submit individual.dateOfBirth as { "day", "month", "year" } |
ssnLast4 | Submit individual.ssnLast4 |
fullSsn | Submit individual.fullSsn |
addressLine1, addressCity, addressSubdivision, addressPostalCode, addressCountryCode | Submit the corresponding individual.address.* fields |
email | Submit email |
phoneNumber | Submit phone |
citizenship | Submit individual.citizenship |
purposeOfAccount, sourceOfFunds | Submit individual.purposeOfAccount, individual.sourceOfFunds |
employmentStatus, occupation, expectedVolume | Submit corresponding individual.* fields |
tos | Present ToS to customer and submit tosAcceptances. See Terms of Service below |
Requirement keys use canonical field names (e.g., addressSubdivision, addressPostalCode), which map to the address shape in the request body (state, postCode). A requirement keyed addressSubdivision is resolved by submitting individual.address.state.
The resolution loop
A typical requirements-resolution pattern:
async function ensureCapabilityActive(customerId, requiredCapability) {
let customer = await getCustomer(customerId);
while (customer.capabilities[requiredCapability]?.status !== 'active') {
// Find requirements affecting this capability that need action
const actionable = Object.entries(customer.requirements)
.filter(([, req]) => req.impact?.includes(requiredCapability))
.filter(([, req]) => ['due', 'rejected', 'overdue'].includes(req.status));
if (actionable.length === 0) {
// All requirements submitted; wait for async verification
await waitForCapabilityUpdate(customerId, requiredCapability);
customer = await getCustomer(customerId);
continue;
}
for (const [fieldName, req] of actionable) {
if (fieldName === 'tos') {
// ToS URL is included in the requirement; present it to the customer
await presentTosAndSubmitAcceptance(customerId, req.url);
continue;
}
// All other requirements resolve by resubmitting the corresponding identity field
const value = await collectFromCustomer(fieldName);
await postCustomerUpdate(customerId, buildPatch(fieldName, value));
}
// Use webhooks in production instead of polling
await waitForCapabilityUpdate(customerId, requiredCapability);
customer = await getCustomer(customerId);
}
}
Special cases
Terms of Service
When a tos requirement is present, the requirement entry includes a url with the current Terms of Service link:
- Read the
url from requirements.tos and render the Terms of Service to the customer. This is required; do not skip.
- Submit the acceptance:
POST /v2/customers/{customerId}
{
"tosAcceptances": [
{
"versionId": "us_individual_2026-05-29",
"language": "en",
"acceptedAt": "2026-05-01T10:01:00Z"
}
]
}
Rejected requirements
A requirement with status: rejected means the submitted value failed verification. Collect the field again from the customer and resubmit via POST /v2/customers/{customerId}.
What you cannot resolve
Some inactive capability states are driven by compliance decisions that cannot be unlocked via the API:
- Sanctions matches
- Adverse media
- Manual compliance holds
Never expose compliance-specific reasons to end-users. Show a generic message such as “We’re unable to complete this request. Please contact support.” Coinbase handles these compliance decisions so you don’t have to make or surface them in your product.
Monitoring via webhooks
Subscribe to customer.capability.status_changed to be notified when requirements are resolved and capabilities transition. This avoids polling the customer endpoint in your integration.
{
"type": "customer.capability.status_changed",
"data": {
"customerId": "customer_af2937b0-...",
"capability": "custodyFiat",
"previousStatus": "pending",
"status": "active",
"requirements": {}
}
}
Webhook event names are provisional and subject to change until the webhooks documentation ships.