Core ConceptsCustomers

Customers

Individuals and businesses, and how the customer object doubles as the verification intake.

One object, two kinds

A customer is your end user. kind is required, and there is no default: a silently defaulted individual for a forgotten field is the wrong record to fail into.

{
  "kind": "individual",
  "first_name": "Ana",
  "last_name": "Silva",
  "email": "ana@example.com",
  "phone": "+14155550123",
  "date_of_birth": "1993-04-12",
  "nationality": "US",
  "residence_country": "US",
  "address": {
    "street_line_1": "300 Post St",
    "city": "San Francisco",
    "state": "CA",
    "postal_code": "94108",
    "country": "US"
  },
  "id_document": {"kind": "passport", "number": "P123456"},
  "economic_profile": {"tin": "078-05-1120", "tax_residence_country": "US"},
  "tos_acceptance": {"ip_address": "203.0.113.7"},
  "partner_reference_id": "user-8213"
}

Only a terms path is strictly required. Names are not, so that a partially known person can exist as a legal record before they can transact. In practice, send everything you have: date of birth, nationality, and address anchor the identity cross-checks, and anything you omit surfaces later in a requirement bucket.

Responses omit the other kind's fields entirely. An individual payload has no legal_name key at all, rather than a null.

The customer object is the verification intake

Two intake-only arrays ride customer create and PATCH. They are archived and staged for the customer's single applicant, never stored as customer fields.

{
  "identifying_information": [
    {
      "kind": "passport",
      "issuing_country": "USA",
      "number": "P123456",
      "image_front": "<base64>",
      "image_back": "<base64, optional>"
    }
  ],
  "documents": [
    {"purpose": "selfie", "file": "<base64>"},
    {"purpose": "proof_of_address", "file": "<base64>"}
  ]
}
  • identifying_information[].kind: passport, id_card, drivers_license, or residence_permit. issuing_country is required on every entry, because a country is required per document, and failing here names the field instead of failing opaquely at submit.
  • documents[].purpose: selfie, proof_of_address, registration_document, or other.
  • Images are base64, and data: URIs are accepted. Up to ten of each per request.

Nothing here enters review on its own. Documents are staged. The only call that submits them is POST /api/customers/{id}/verifications/submit. See Verification & Endorsements.

Requesting endorsements

Enrollment is explicit. Naming an endorsement is what starts that currency's compliance review at all:

{"endorsements": ["endo_usd"]}

Requesting endo_usd requires email, phone, economic_profile.tin, and economic_profile.tax_residence_country in the same request. Missing ones return 422 endorsement_profile_required naming them, in sandbox as well as live, so integrations learn the contract before go-live. You can request endorsements later with a PATCH.

Reading a customer

{
  "id": "cus_033y1FhTOmoW9W2jAuSvQx",
  "object": "customer",
  "kind": "individual",
  "first_name": "Ana",
  "last_name": "Silva",
  "email": "ana@example.com",
  "address": {"state": "CA", "country": "US"},
  "status": "active",
  "tos": {
    "status": "accepted",
    "tos_version": "2026-08-01",
    "accepted_at": "2026-08-26T09:14:40+00:00"
  },
  "endorsements": [
    {
      "name": "endo_usd",
      "status": "approved",
      "requirements": {
        "complete": ["terms_of_service", "identity", "selfie", "proof_of_address", "aml"],
        "uploaded": [],
        "pending": [],
        "missing": [],
        "issues": []
      }
    }
  ],
  "partner_reference_id": "user-8213",
  "created_at": "2026-08-26T09:14:40+00:00"
}
  • endorsements is the KYC reading. One entry per endorsement you requested. Its status is the gate, and its requirements buckets show each item's exact state. Every entry is a bare documented code, never prose.
  • status is the account itself: pending, active, rejected, or frozen. A frozen customer's calls are refused.

The payload is deliberately lean. Identity inputs you send (date of birth, phone, documents, economic profile) are relayed for verification and never echoed back, and the address trims to state and country. What you submitted is yours to keep: the API reflects readiness, not a copy of the dossier.

Updating

PATCH /api/customers/{id} updates identity fields and accepts the same two document arrays. Supplying what a missing or issues bucket asks for is an ordinary PATCH, followed by another submit.

Two PATCH semantics worth knowing:

  • Nested objects merge. {"address": {"state": "NY"}} fixes the state alone. Sending the whole object replaces every line it names, and "address": null clears it.
  • Approved verification data is never overwritten by a later review. Your PATCH is always the correction path.

Set partner_reference_id to your own user id at creation. It is unique per environment within your account, filterable on list endpoints, and the cleanest way to join Rasto records to yours. A collision returns 409 partner_reference_id_exists.