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

# Individual quickstart

> Onboard a natural person in sandbox, open a USD account for them, fund it, and pay out. One linear page.

This page onboards an **individual**: a natural person who opens an account in their own name. It goes from requirements discovery to a completed payout. The business path is the [Sandbox quickstart](/sandbox/quickstart). For what an individual owes, see [Onboarding an Individual](/kyb/individuals).

## Prerequisites

* A sandbox API key (`ck_sandbox_...`) and a registered webhook endpoint. Set them up as in the [Sandbox quickstart → Prerequisites](/sandbox/quickstart#prerequisites).
* Individual onboarding must be enabled for your organization. Contact your Conduit account manager. Until it is, Steps 1 and 3 return `422` [`SUBJECT_REQUIREMENTS_NOT_AVAILABLE`](/errors#subject-requirements-not-available).
* `curl`, `jq` and `uuidgen` on your PATH.

```bash theme={null}
export SANDBOX_HOST="https://api.sandbox.conduit.financial"
export SANDBOX_API_KEY="ck_sandbox_..."

# Only the magic bytes are validated in sandbox, so a stub file works.
printf '%%PDF-1.4\nfixture\n%%%%EOF\n' > identity-verification-report.pdf
printf '%%PDF-1.4\nfixture\n%%%%EOF\n' > invoice.pdf
```

## Step 1 - Discover what the person owes

Send `subjectType=individual`. For an individual, `country` is the person's **country of residence**.

```bash theme={null}
curl -s "${SANDBOX_HOST}/v2/onboarding/requirements?country=USA&subjectType=individual" \
  -H "x-api-key: ${SANDBOX_API_KEY}"
```

The response (abbreviated: each `allowedValues` list and most fields are cut):

```json theme={null}
{
  "schemaVersion": "3",
  "context": "onboarding",
  "country": "USA",
  "fields": [
    { "pointer": "/activity/occupation", "label": "Occupation", "type": "string", "required": true, "group": "activity" },
    { "pointer": "/activity/accountPurpose", "label": "How will you use your account?", "type": "enumArray", "required": true, "group": "activity", "allowedValues": ["Investment", "..."] },
    { "pointer": "/activity/expectedMonthlyVolume", "label": "Expected Monthly Volume", "type": "enum", "required": true, "group": "activity", "allowedValues": ["VOLUME_LT_10K", "VOLUME_10K_50K", "..."] },
    { "pointer": "/regulatoryHistory/isPoliticallyExposedPerson", "label": "Politically Exposed Person?", "type": "boolean", "required": true, "group": "regulatoryHistory" },
    { "pointer": "/certification/termsAndConditions", "label": "Terms and Conditions", "type": "boolean", "required": true, "group": "certification", "mustEqual": true },
    "..."
  ],
  "documents": [],
  "minDocuments": 0,
  "individualRequirements": [
    {
      "role": "ACCOUNT_HOLDER",
      "minCount": 1,
      "maxCount": 1,
      "fields": [
        { "pointer": "/firstName", "label": "First Name", "type": "string", "required": true },
        { "pointer": "/phoneNumber", "label": "Phone Number", "type": "phone", "required": true },
        { "pointer": "/birthDate", "label": "Date of Birth", "type": "date", "required": true, "constraints": { "minDate": "1900-01-01", "minAgeYears": 18 } },
        { "pointer": "/taxIdType", "label": "Tax ID Type", "type": "enum", "required": true, "conditions": [{ "pointer": "/nationality", "operator": "in", "values": ["USA", "US"], "scope": "person" }], "allowedValues": ["SSN"] },
        "..."
      ],
      "documents": [
        { "canonicalType": "IDENTITY_VERIFICATION_ATTESTATION", "title": "Identity Verification Attestation (Reliance)" }
      ]
    }
  ]
}
```

Top-level `fields[]` are the account holder's activity, politically-exposed-person and consent answers. The one `ACCOUNT_HOLDER` row carries the person's own fields and documents. A field with `conditions` is required only while its conditions hold: here the tax ID follows the person's `nationality`.

## Step 2 - Upload the identity verification report

You verify the person with your own identity-verification provider. Upload the provider's report with `purpose=kyc`. See [Person-level evidence](/kyb/document-types#person-level-evidence) for what it must contain.

```bash theme={null}
IDV_DOC_ID=$(curl -s -X POST "${SANDBOX_HOST}/v2/documents" \
  -H "x-api-key: ${SANDBOX_API_KEY}" \
  -H "idempotency-key: $(uuidgen)" \
  -F "file=@./identity-verification-report.pdf;type=application/pdf" \
  -F "purpose=kyc" | jq -r '.id')
```

`201 Created` returns `{ "id": "doc_...", "purpose": "kyc", "classificationStatus": "pending", ... }`.

## Step 3 - Submit the application

One person, the account holder, with the report id in their `documentIds[]`. No business envelope.

```bash theme={null}
APP_ID=$(curl -s -X POST "${SANDBOX_HOST}/v2/onboarding" \
  -H "x-api-key: ${SANDBOX_API_KEY}" \
  -H "idempotency-key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "subjectType": "individual",
    "clientReferenceId": "jane-doe-001",
    "ownership": {
      "persons": [{
        "roles": ["ACCOUNT_HOLDER"],
        "firstName": "Jane",
        "lastName": "Doe",
        "email": "jane.doe@example.com",
        "phoneNumber": "(408) 555-0166",
        "birthDate": "1985-04-12",
        "nationality": "USA",
        "taxIdType": "SSN",
        "taxIdNumber": "524-45-6701",
        "taxIdCountry": "USA",
        "address": { "country": "USA", "addressLine1": "845 Alma St", "city": "Palo Alto", "state": "US-CA", "postalCode": "94301" },
        "documentIds": ["'"${IDV_DOC_ID}"'"]
      }]
    },
    "activity": {
      "occupation": "Software Engineer",
      "accountPurpose": ["Investment"],
      "sourceOfFunds": ["Personal/Founder Funds"],
      "expectedMonthlyVolume": "VOLUME_10K_50K",
      "expectedTransactionsPerMonth": "10-50",
      "countriesOfActivity": ["USA"]
    },
    "regulatoryHistory": { "isPoliticallyExposedPerson": false },
    "certification": { "termsAndConditions": true }
  }' | jq -r '.id')
```

`202 Accepted` returns the application in `processing`. The phone above is in national format, which the API accepts because the person's `address.country` completes it ([phone numbers](/kyb/individuals#phone-number)).

A second submission for the same tax ID is refused with `409`, also when it differs only in dashes or spaces: see [Submit an individual application](/guides/onboard-customer#submit-an-individual-application).

## Step 4 - Approve and read the customer

Drive the decision with the [sandbox decision lever](/sandbox/customer-kyc#simulate-endpoints), then wait for the `customerId`:

```bash theme={null}
curl -s -X POST "${SANDBOX_HOST}/v2/sandbox/applications/${APP_ID}/simulate/decision" \
  -H "x-api-key: ${SANDBOX_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{ "outcome": "approved" }'

for i in {1..20}; do
  CUSTOMER_ID=$(curl -s "${SANDBOX_HOST}/v2/applications/${APP_ID}" \
    -H "x-api-key: ${SANDBOX_API_KEY}" | jq -r '.customerId // empty')
  [ -n "${CUSTOMER_ID}" ] && break
  sleep 1
done
: "${CUSTOMER_ID:?never appeared: check the decision response above}"

curl -s "${SANDBOX_HOST}/v2/customers/${CUSTOMER_ID}" -H "x-api-key: ${SANDBOX_API_KEY}"
```

```json theme={null}
{
  "id": "cus_034YPzHCifwr95Ryjb0B68",
  "clientReferenceId": "jane-doe-001",
  "applicationId": "app_034YPx5mMp0B6I9kOGf5VU",
  "createdAt": "2026-10-01T14:49:32.100Z",
  "updatedAt": "2026-10-01T14:49:32.100Z",
  "features": [],
  "isHouseAccount": false,
  "type": "individual",
  "individualId": "ind_034YPzHDOqgcFNfGILrQbR",
  "firstName": "Jane",
  "lastName": "Doe",
  "email": "jane.doe@example.com",
  "phone": "(408) 555-0166",
  "birthDate": "1985-04-12",
  "nationality": "US",
  "taxIdType": "SSN",
  "taxIdCountry": "US",
  "address": { "addressLine1": "845 Alma St", "city": "Palo Alto", "state": "US-CA", "postalCode": "94301", "country": "US" },
  "activity": {
    "occupation": "Software Engineer",
    "accountPurpose": ["Investment"],
    "sourceOfFunds": ["Personal/Founder Funds"],
    "expectedMonthlyVolume": "VOLUME_10K_50K",
    "expectedTransactionsPerMonth": "10-50",
    "countriesOfActivity": ["USA"]
  },
  "regulatoryHistory": { "isPoliticallyExposedPerson": false }
}
```

Branch on `type: "individual"`. Countries read back as ISO 3166-1 alpha-2, except `activity.countriesOfActivity`, which keeps the alpha-3 codes you sent. The response does not carry the tax ID number.

## Step 5 - Open a USD account

Feature discovery works as for a business. Its `country` is the person's country of residence:

```bash theme={null}
curl -s "${SANDBOX_HOST}/v2/customers/${CUSTOMER_ID}/features/requirements?type=virtual_account&asset=USD" \
  -H "x-api-key: ${SANDBOX_API_KEY}"

curl -s -X POST "${SANDBOX_HOST}/v2/customers/${CUSTOMER_ID}/features" \
  -H "x-api-key: ${SANDBOX_API_KEY}" \
  -H "idempotency-key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "type": "virtual_account", "asset": { "code": "USD" } }'
```

The feature application returns `202` with `status: "approved"`. Within a few seconds the account is `active`:

```bash theme={null}
for i in {1..20}; do
  VAC_ID=$(curl -s "${SANDBOX_HOST}/v2/customers/${CUSTOMER_ID}/virtual-accounts" \
    -H "x-api-key: ${SANDBOX_API_KEY}" | jq -r '[.data[] | select(.status == "active")][0].id // empty')
  [ -n "${VAC_ID}" ] && break
  sleep 1
done
: "${VAC_ID:?no active virtual account appeared: check the feature response above}"
```

Its deposit instructions name the person as beneficiary, at their home address:

```json theme={null}
{
  "type": "us_domestic",
  "currency": "USD",
  "accountNumber": "60215457935098320",
  "beneficiaryName": "Jane Doe",
  "beneficiaryAddress": "845 Alma St\nPalo Alto, US-CA, 94301\nUSA",
  "beneficiaryPostalAddress": { "addressLine1": "845 Alma St", "city": "Palo Alto", "state": "US-CA", "postalCode": "94301", "country": "US" },
  "...": "..."
}
```

## Step 6 - Fund the account

```bash theme={null}
curl -s -X POST "${SANDBOX_HOST}/v2/sandbox/customers/${CUSTOMER_ID}/virtual-accounts/${VAC_ID}/deposits/simulate" \
  -H "x-api-key: ${SANDBOX_API_KEY}" \
  -H "idempotency-key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "assetAmount": { "code": "USD", "amount": "1000.00" } }'

FUNDED=
for i in {1..60}; do
  AVAILABLE=$(curl -s "${SANDBOX_HOST}/v2/customers/${CUSTOMER_ID}/virtual-accounts" \
    -H "x-api-key: ${SANDBOX_API_KEY}" | jq -r --arg id "${VAC_ID}" '.data[] | select(.id == $id) | .balances[0].available.amount')
  [ "${AVAILABLE}" = "1000.00" ] && FUNDED=yes && break
  sleep 2
done
: "${FUNDED:?the deposit was not credited: check it before you pay out}"
```

`202 Accepted` returns `{ "externalReference": "sandbox_..." }`. The deposit is credited within seconds, though the first one after a quiet period can take up to a minute. The account's `available` balance then reads `1000.00`. Wait for it before you pay out. Failure paths are in [Deposits](/sandbox/deposits).

## Step 7 - Pay out

Upload a supporting document, send the payout, then approve the document review. On a US rail the recipient's `bankAddress` is optional: Conduit derives it from `routingNumber`.

```bash theme={null}
DOC_ID=$(curl -s -X POST "${SANDBOX_HOST}/v2/documents" \
  -H "x-api-key: ${SANDBOX_API_KEY}" \
  -F "file=@invoice.pdf;type=application/pdf" \
  -F "purpose=transaction_support" | jq -r '.id')

TXN_ID=$(curl -s -X POST "${SANDBOX_HOST}/v2/payouts" \
  -H "x-api-key: ${SANDBOX_API_KEY}" \
  -H "idempotency-key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "'"${CUSTOMER_ID}"'",
    "virtualAccountId": "'"${VAC_ID}"'",
    "assetAmount": { "code": "USD", "amount": "25.00" },
    "purpose": "payment_for_goods_or_services",
    "documents": ["'"${DOC_ID}"'"],
    "destination": {
      "type": "fiat",
      "rail": "fedwire",
      "recipient": {
        "type": "individual",
        "firstName": "Aiko",
        "lastName": "Tanaka",
        "accountNumber": "000094300000",
        "routingNumber": "021000021",
        "accountType": "checking",
        "bankName": "Chase Bank",
        "phone": "+12125550199",
        "postalAddress": { "addressLine1": "270 Park Ave", "city": "New York", "state": "NY", "postalCode": "10017", "country": "USA" }
      }
    }
  }' | jq -r '.id')

APPROVED=
for i in {1..10}; do
  STATUS=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${SANDBOX_HOST}/v2/sandbox/payouts/${TXN_ID}/simulate-review-approve" \
    -H "x-api-key: ${SANDBOX_API_KEY}" \
    -H "idempotency-key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{}')
  [ "${STATUS}" = "200" ] && APPROVED=yes && break
  sleep 2
done
: "${APPROVED:?the review approval never returned 200: read GET /v2/payouts/${TXN_ID}}"

for i in {1..30}; do
  PAYOUT_STATUS=$(curl -s "${SANDBOX_HOST}/v2/payouts/${TXN_ID}" \
    -H "x-api-key: ${SANDBOX_API_KEY}" | jq -r '.status')
  case "${PAYOUT_STATUS}" in completed|failed|cancelled) break ;; esac
  sleep 2
done
COMPLETED=$([ "${PAYOUT_STATUS}" = "completed" ] && echo yes)
: "${COMPLETED:?the payout ended ${PAYOUT_STATUS}: read GET /v2/payouts/${TXN_ID}}"
```

The document review opens a few seconds after the payout is created, and the `transaction.under_review` webhook tells you it is open. A review approval sent before that returns `404 PAYOUT_NOT_FOUND`, so the loop above sends it again. Sending it again is safe. After a `200` the payout resumes, and the last loop waits for its terminal status. A completed `GET /v2/payouts/${TXN_ID}` reads `status: "completed"`, with `source.assetAmount` `25.25`, `destination.assetAmount` `25.00` and a `0.25` fixed fee in `fees[]`. The account's `available` balance reads `974.75`. Fee rules and the rest of the payout lifecycle are in [Send a payout](/guides/send-payout).

## See also

* [Onboarding an Individual](/kyb/individuals): what a person owes
* [Customer KYC (Sandbox)](/sandbox/customer-kyc): rejection options for an individual application
* [Sandbox quickstart](/sandbox/quickstart): the business path
