> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cdp.coinbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Requirements

> Requirements are the outstanding KYC actions a customer must complete to unlock a capability. Learn how to read, resolve, and test them.

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:

```json theme={null}
"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](#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`    |

<Note>
  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](/api-reference/v2/rest-api/customers/create-a-customer)) as the source of truth rather than this table.
</Note>

## 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](#terms-of-service)  |
| `taxAttestation`                                                                                               | `taxAttestations` — see [Tax attestations](#tax-attestations) |

<Note>
  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`.
</Note>

## The resolution loop

A typical pattern: submit outstanding requirements, then wait for the affected capability to leave `pending`.

```javascript theme={null}
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](/customers-kyc/capabilities#webhooks) 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).

1. Render each version's `url` to the customer and capture acceptance.
2. Submit one acceptance per required version:

```json theme={null}
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 `versionId`s 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:

```json theme={null}
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.

<Warning>
  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.
</Warning>

## 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 |

```json theme={null}
PUT /v2/customers/{customerId}
{
  "individual": {
    "fullSsn": "000-00-0000"
  }
}
```

<Note>
  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.
</Note>

<Warning>
  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.
</Warning>
