Skip to main content
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"] }
}
FieldDescription
keyThe field name that is required (e.g., fullSsn, addressSubdivision, tos)
statusCurrent state. See Requirement statuses below
deadlineOptional. Deadline for submission (ISO 8601)
impactWhich capabilities this requirement is blocking

Requirement statuses

StatusMeaning
eventually_duePossible future requirement; no action needed yet
dueMust be submitted (may have a deadline)
pendingSubmitted, awaiting verification
rejectedVerification failed; resubmit the field
overdueDeadline 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 keyResolved by
firstName, lastNameSubmit individual.firstName, individual.lastName
dateOfBirthSubmit individual.dateOfBirth as { "day", "month", "year" }
ssnLast4Submit individual.ssnLast4
fullSsnSubmit individual.fullSsn
addressLine1, addressCity, addressSubdivision, addressPostalCode, addressCountryCodeSubmit the corresponding individual.address.* fields
emailSubmit email
phoneNumberSubmit phone
citizenshipSubmit individual.citizenship
purposeOfAccount, sourceOfFundsSubmit individual.purposeOfAccount, individual.sourceOfFunds
employmentStatus, occupation, expectedVolumeSubmit corresponding individual.* fields
tosPresent 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:
  1. Read the url from requirements.tos and render the Terms of Service to the customer. This is required; do not skip.
  2. 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.