Requirements are the outstanding actions a customer must complete before a requested capability becomes active. They appear as a map on the customer object, keyed by field name, with each entry describing its status and which capabilities it affects. Requirements are only shown for capabilities that have been requested, and when a requirement is satisfied it disappears from the map.
Identity verification runs asynchronously. After you submit the required information, Coinbase evaluates it, and the affected capabilities transition out of pending once verification completes.
Requirements map
requirements is keyed by field name; the key itself is the requirement:
"requirements": {
"fullSsn": { "status": "rejected", "impact": ["tradeCrypto"] },
"sourceOfFunds": { "status": "due" },
"citizenship": { "status": "due" },
"tos": {
"status": "due",
"impact": ["tradeCrypto", "transferFiat"],
"tosVersions": [
{
"versionId": "us_individual_2026-05-29",
"languages": ["en"],
"url": "https://docs.cdp.coinbase.com/legal/terms/us_individual_2026-05-29"
}
]
}
}
| Field | Description |
|---|
| key | The field name that is required (e.g. fullSsn, addressSubdivision, tos, taxAttestation) |
status | Current state — see Requirement statuses |
deadline | Optional ISO 8601 deadline by which the requirement must be satisfied |
impact | Capabilities this requirement is blocking (sorted alphabetically) |
tosVersions | Present only on the tos key — the Terms of Service versions still to accept |
taxForms | Present only on the taxAttestation key — the tax forms still to complete |
Requirement statuses
| Status | Meaning |
|---|
due | Must be submitted |
pending | Submitted, awaiting verification |
rejected | Verification failed — resubmit the field |
When verification passes, the requirement is removed from the map entirely. Requirements in due or rejected need action from you; pending requirements are being processed asynchronously.
Which capabilities require which requirements
You never have to hard-code this mapping — a customer’s requirements map only ever lists what their requested capabilities still need, and each entry’s impact array names the capabilities it is blocking. The table below is provided as a planning reference for what to collect up front.
Most capabilities share a common baseline set. tradeCrypto requires additional due-diligence information on top of that baseline.
| Requirement | Required for |
|---|
firstName, lastName | All capabilities |
dateOfBirth | All capabilities |
fullSsn | All capabilities |
address (line, city, state, postal code, country) | All capabilities |
email | All capabilities |
phoneNumber | All capabilities |
purposeOfAccount | All capabilities |
sourceOfFunds | All capabilities |
tos | All capabilities |
taxAttestation (W-9) | All capabilities |
citizenship | tradeCrypto |
employmentStatus | tradeCrypto |
occupation | tradeCrypto |
expectedVolume | tradeCrypto |
This mapping is determined by Coinbase’s compliance policy and is subject to change — requirements may be added, removed, or re-scoped to different capabilities over time, and they can vary by the customer’s jurisdiction. Always treat the live requirements map on the customer object (and the API reference) as the source of truth rather than this table.
Resolving requirements
Every field-based requirement is resolved by submitting the corresponding field under individual via PUT /v2/customers/{customerId}. The two non-field requirements — tos and taxAttestation — are resolved with their own arrays.
| Requirement key | Resolved by |
|---|
firstName, lastName | individual.firstName, individual.lastName |
dateOfBirth | individual.dateOfBirth as { "day", "month", "year" } |
ssnLast4 | individual.ssnLast4 |
fullSsn | individual.fullSsn |
addressLine1, addressLine2, addressCity, addressSubdivision, addressPostalCode, addressCountryCode | The corresponding individual.address.* field |
email | individual.email |
phoneNumber | individual.phoneNumber |
citizenship | individual.citizenship |
purposeOfAccount, sourceOfFunds, employmentStatus, occupation, expectedVolume | The corresponding individual.* field |
tos | tosAcceptances — see Terms of Service |
taxAttestation | taxAttestations — see Tax attestations |
Requirement keys use canonical field names that map to the address shape in the request body: addressSubdivision resolves with individual.address.state, and addressPostalCode resolves with individual.address.postCode.
The resolution loop
A typical pattern: submit outstanding requirements, then wait for the affected capability to leave pending.
async function ensureCapabilityActive(customerId, requiredCapability) {
let customer = await getCustomer(customerId);
while (customer.capabilities[requiredCapability]?.status === "pending") {
// Requirements that are blocking this capability and need action.
const actionable = Object.entries(customer.requirements)
.filter(([, req]) => req.impact?.includes(requiredCapability))
.filter(([, req]) => req.status === "due" || req.status === "rejected");
if (actionable.length === 0) {
// Everything submitted — wait for async verification (use webhooks in production).
await waitForCapabilityChange(customerId, requiredCapability);
customer = await getCustomer(customerId);
continue;
}
for (const [key] of actionable) {
if (key === "tos") {
await presentTosAndSubmitAcceptances(customerId, customer.requirements.tos.tosVersions);
} else if (key === "taxAttestation") {
await submitTaxAttestations(customerId, customer.requirements.taxAttestation.taxForms);
} else {
// Field requirement: collect and resubmit the corresponding identity field.
await submitIdentityField(customerId, key);
}
}
await waitForCapabilityChange(customerId, requiredCapability);
customer = await getCustomer(customerId);
}
}
In production, drive this off the customers.capability.changed webhook instead of polling.
Terms of Service
When a tos requirement is present, requirements.tos.tosVersions[] lists each Terms of Service version the customer must still accept, with a stable versionId, the languages it is published in, and a url to render (append ?lang=<tag> for a specific translation).
- Render each version’s
url to the customer and capture acceptance.
- Submit one acceptance per required version:
PUT /v2/customers/{customerId}
{
"tosAcceptances": [
{
"versionId": "us_individual_2026-05-29",
"language": "en",
"acceptedAt": "2026-04-10T12:00:00Z"
}
]
}
language must be one of the version’s languages, or the request is rejected with unsupported_tos_language. When new versions are published, only the new versionIds appear in tosVersions[] — submit acceptances for just those.
Tax attestations
Some capabilities require a tax attestation. When a taxAttestation requirement is present, requirements.taxAttestation.taxForms[] lists the forms the customer must complete (for example, us_w9). Submit one taxAttestations entry per required form:
PUT /v2/customers/{customerId}
{
"taxAttestations": [
{
"form": "us_w9",
"isExemptBackupWithholding": true,
"edeliveryConsent": true,
"acceptedAt": "2026-04-17T20:00:00Z"
}
]
}
The fields required per entry depend on form; the example above shows IRS Form W-9. Tax attestations are ingestion-only and never returned on read.
Rejected requirements
A requirement with status: rejected means the submitted value failed verification. Collect the field from the customer again and resubmit it via PUT /v2/customers/{customerId}. Resubmitting a corrected value starts a new verification.
What you cannot resolve
Some capabilities become inactive because of a terminal compliance decision (for example, a sanctions watchlist match). These cannot be unlocked through the API, and no requirement resubmission will clear them.
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 makes and manages these compliance decisions on your behalf.
Ongoing monitoring
Compliance does not stop at onboarding. Coinbase continuously monitors customers and may flag one for KYC re-verification. When that happens you receive a customers.kyc_refresh.flagged webhook carrying a deadline; capabilities may remain active until the deadline and are disabled afterward until the customer resubmits the requested information. A customers.kyc_refresh.completed webhook is emitted once the refresh is satisfied.
Testing in sandbox
In sandbox and test environments, Coinbase recognizes magic SSN values that force a deterministic verification outcome so you can exercise each capability state end-to-end without real PII. Submit them as individual.fullSsn (or individual.ssnLast4).
| Outcome | fullSsn | ssnLast4 | Effect |
|---|
| Approve | 000-00-0000 | 0000 | Requested capabilities become active; the satisfied requirements disappear |
| Decline (recoverable) | 000-00-0001 | 0001 | Affected capabilities stay pending and the requirements become rejected; the customer can resubmit corrected PII |
| Block (terminal) | 000-00-0002 | 0002 | Simulates a sanctions block: all requested capabilities become inactive and their requirements disappear; this is terminal and cannot be resubmitted |
PUT /v2/customers/{customerId}
{
"individual": {
"fullSsn": "000-00-0000"
}
}
The recoverable decline (...0001) is the only outcome you can retry — resubmitting a passing value starts a new verification and can activate the capability. The block (...0002) is terminal: resubmitting a passing SSN afterward does not lift it.
Magic values only apply in sandbox and test environments. In production they are treated as real PII and verified normally — never rely on them outside of testing.