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

# Onboard a business entity via the API

> Create a business entity with a global API key and complete KYB: business details, representatives, KYC, documents, and submission via next_step.

A **global** (user-level) API key can create a business entity and run its KYB
onboarding without touching the dashboard: business details, the representatives
(beneficial owners and officers), each representative's identity verification,
supporting documents, and the final submission. Every response includes a
`next_step` object that tells you what's outstanding, so you don't infer it from
status.

<Note>
  This is **self-serve** onboarding for entities your key owns. The key's user is
  the creator and admin, not a representative of the entity. To onboard entities
  on behalf of *your* customers as a platform, use
  [Partner Onboarding](/api-reference/partner-onboarding/create-application) instead.
</Note>

## You'll need

* A **global** API key (user-scoped, not entity-scoped) with the
  `entity:create` scope for writes and `accounts:read` to read and list
  onboarding state. Onboarding routes reject entity-scoped keys with `403`.
* No `x-entity-id` header. The entity is addressed in the URL path.

## Read `next_step` after every call

Every onboarding read returns a `next_step`. For a US entity mid-onboarding it
looks like this (`address` is a US-only requirement — see
[documents](#5-upload-supporting-documents)):

```json theme={null}
{
  "step": "manage_representatives",
  "phase": "business_info",
  "missing": ["Primary representative"],
  "required_proof_types": ["incorporation", "address"],
  "can_submit": false
}
```

| Field                  | Meaning                                                                                        |
| ---------------------- | ---------------------------------------------------------------------------------------------- |
| `step`                 | The step the application is on now.                                                            |
| `phase`                | `business_info` → `ready_to_submit` → `submitted`.                                             |
| `missing`              | The items still required to clear the current step.                                            |
| `required_proof_types` | KYB documents still owed, as `proof_type` values. Pass each one when requesting an upload URL. |
| `can_submit`           | `true` once everything required has been collected.                                            |

The loop: call an endpoint, then `GET /entities/{entity_id}`, act on
`next_step.step`, repeat until `can_submit` is `true`, then submit.

| `step`                                                   | Do this                                                                                                      |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `collect_business_details`, `collect_business_addresses` | [Create the entity](#1-create-the-entity), or [update its details](#2-update-business-details).              |
| `collect_additional_details`                             | [Update business details](#2-update-business-details) with the account purpose and digital-currency answers. |
| `manage_representatives`                                 | [Add representatives](#3-add-representatives), marking one `is_primary`.                                     |
| `complete_identity_verification`                         | [Verify each representative](#4-verify-each-representative).                                                 |
| `upload_documents`                                       | [Upload the documents](#5-upload-supporting-documents) named in `required_proof_types`.                      |
| `submit_application`                                     | [Submit the application](#6-submit-the-application).                                                         |

<Note>
  Some entities draw an extra due-diligence step (`complete_due_diligence`) or a
  follow-up information request after submission (`resolve_info_requests`). These
  are handled in the Meow dashboard; the API surfaces them in `next_step` so you
  know when to direct the business there.
</Note>

## 1. Create the entity

`POST /entities` creates the entity and returns its public ID. The creating user
is granted admin, so the same key can act on the entity immediately. Send an
empty body to create a bare entity, or seed any KYB business details up front.

```bash theme={null}
curl -X POST https://api.meow.com/v1/entities \
  -H "x-api-key: $MEOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "business_name": "Catnip Coffee Co",
    "business_date_of_incorporation": "2021-03-01",
    "legal_structure": "ccorp",
    "incorporation_state": "DE",
    "business_address": {
      "address": "9 Whisker Way",
      "city": "San Francisco",
      "state": "CA",
      "zip": "94105",
      "country": "US"
    }
  }'
```

```json Response theme={null}
{
  "id": "b1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "business_name": "Catnip Coffee Co",
  "created_at": "2026-06-26T18:04:11.512Z"
}
```

Save `id`. It's the `{entity_id}` for every call below, and the `x-entity-id`
header once the entity is onboarded and you call entity-scoped APIs.

`legal_structure` is one of: `ccorp`, `corp`, `scorp`, `llc`, `llp`, `lp`,
`partnership`, `soleprop`, `nonprofit`, `trust`, `estate`, `foreign_entity`,
`scheme`.

<Tip>
  Pass your own `reference_id` to make creation idempotent. Retrying a create with
  a `reference_id` you've already used returns the original entity instead of
  creating a duplicate, so a network timeout never leaves you with two entities.
</Tip>

## 2. Update business details

`PATCH /entities/{entity_id}/business-details` fills in or corrects KYB details
before the application is submitted. Send only the fields you want to change;
anything you omit keeps its current value. Use it for the business profile the
create call didn't include, and to answer the account-purpose and
digital-currency questions (`collect_additional_details`).

```bash theme={null}
curl -X PATCH https://api.meow.com/v1/entities/$ENTITY_ID/business-details \
  -H "x-api-key: $MEOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "business_phone": "+14155550123",
    "business_taxid": "12-3456789",
    "business_website": "https://catnipcoffee.com",
    "business_description": "Specialty coffee roaster and cafe",
    "industry": "limited_service_restaurants",
    "account_purpose": "business_operations",
    "provides_blockchain_or_digital_currency_services": false,
    "transacts_in_digital_currency": false,
    "develops_governs_supports_blockchain_protocol": false
  }'
```

The response is the refreshed onboarding status, including the updated
`next_step`. Send `physical_address` together with `business_address`; omitting
it keeps any physical address already on file, or uses the legal address when
none is set.

## 3. Add representatives

`POST /entities/{entity_id}/representatives` records a beneficial owner or
officer and returns its `representative_id`. This step is **create-only**.
Verify the representative separately (section 4).

Mark exactly one representative as the signer with `is_primary: true`; setting it
demotes any previous primary. **A primary representative is required before the
application can be submitted.**

```bash theme={null}
curl -X POST https://api.meow.com/v1/entities/$ENTITY_ID/representatives \
  -H "x-api-key: $MEOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "felix@catnipcoffee.com",
    "first_name": "Felix",
    "last_name": "Whiskers",
    "title_id": "chief executive officer",
    "is_beneficial_owner": true,
    "percent_ownership": "50",
    "is_primary": true
  }'
```

```json Response theme={null}
{
  "representative_id": "a7c3...",
  "email": "felix@catnipcoffee.com",
  "title_id": "chief executive officer",
  "is_beneficial_owner": true,
  "percent_ownership": "50",
  "is_primary": true
}
```

* `title_id` is one of: `chief executive officer`, `chief financial officer`,
  `chief operating officer`, `president`, `general partner`, `managing member`,
  `finance manager`.
* `percent_ownership` is required when `is_beneficial_owner` is `true`, between
  `25` and `100`. Total beneficial ownership across representatives cannot
  exceed `100`.
* Each `email` is unique per entity. Re-posting the same address returns `400`.

## 4. Verify each representative

KYC is **per representative**, with two ways to do it. Pick one per person.

<Tabs>
  <Tab title="You hold their details">
    Submit the representative's identity data and Meow runs verification on it.

    ```bash theme={null}
    curl -X POST https://api.meow.com/v1/entities/$ENTITY_ID/representatives/$REP_ID/kyc \
      -H "x-api-key: $MEOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "first_name": "Felix",
        "last_name": "Whiskers",
        "date_of_birth": "1990-01-01",
        "id_number": "123-45-6789",
        "id_type": "us_ssn",
        "phone_number": "+14155550123",
        "address": {
          "address": "1 Catnip Court",
          "city": "San Francisco",
          "state": "CA",
          "zip": "94105",
          "country": "US"
        }
      }'
    ```

    ```json Response theme={null}
    { "representative_id": "a7c3...", "kyc_status": "pending" }
    ```

    For `id_type: us_ssn` send nine digits with or without dashes
    (`123-45-6789` or `123456789`); for `us_ssn_last_4` send the last four.
  </Tab>

  <Tab title="They self-verify">
    Mint a shareable verification link and send it to the representative; their
    KYC completes when they finish it.

    ```bash theme={null}
    curl -X POST https://api.meow.com/v1/entities/$ENTITY_ID/representatives/$REP_ID/verification-link \
      -H "x-api-key: $MEOW_API_KEY"
    ```

    ```json Response theme={null}
    {
      "representative_id": "a7c3...",
      "identity_verification_url": "https://verify.meow.com/v/8f2a1c..."
    }
    ```
  </Tab>
</Tabs>

Verification completes asynchronously. Track it via the entity's
[`next_step`](#7-track-progress-to-submission) (the `complete_identity_verification`
step clears once a representative is verified) or the `identity_verification.*`
webhooks. The entity's own `kyc_status` reflects the primary user, not an added
representative.

## 5. Upload supporting documents

`next_step.required_proof_types` lists exactly which documents the entity still
owes. For a US entity that's typically `["incorporation", "address"]`. Each
one uses a three-step flow: get a pre-signed URL, `PUT` the bytes, then
confirm. Repeat the flow for each proof type — for example, the incorporation
certificate first, then the proof of physical address:

<Steps>
  <Step title="Request an upload URL">
    ```bash theme={null}
    curl -X POST https://api.meow.com/v1/entities/$ENTITY_ID/documents/upload-url \
      -H "x-api-key: $MEOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "proof_type": "incorporation", "file_name": "certificate.pdf", "content_type": "application/pdf" }'
    ```

    ```json Response theme={null}
    {
      "upload_url": "https://s3.amazonaws.com/...&X-Amz-Signature=...",
      "object_key": "kyb/b1b2.../certificate.pdf",
      "max_bytes": 10485760,
      "expires_in_seconds": 900
    }
    ```
  </Step>

  <Step title="PUT the file to upload_url">
    ```bash theme={null}
    curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @certificate.pdf
    ```

    Use the same `Content-Type` you requested, keep the file under `max_bytes`, and
    upload before the URL expires.
  </Step>

  <Step title="Confirm the upload">
    ```bash theme={null}
    curl -X POST https://api.meow.com/v1/entities/$ENTITY_ID/documents/confirm \
      -H "x-api-key: $MEOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "object_key": "kyb/b1b2.../certificate.pdf", "proof_type": "incorporation" }'
    ```

    ```json Response theme={null}
    { "confirmed": true, "file_name": "certificate.pdf" }
    ```
  </Step>

  <Step title="Repeat for the proof of physical address">
    ```bash theme={null}
    curl -X POST https://api.meow.com/v1/entities/$ENTITY_ID/documents/upload-url \
      -H "x-api-key: $MEOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "proof_type": "address", "file_name": "utility-bill.pdf", "content_type": "application/pdf" }'
    curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @utility-bill.pdf
    curl -X POST https://api.meow.com/v1/entities/$ENTITY_ID/documents/confirm \
      -H "x-api-key: $MEOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "object_key": "kyb/b1b2.../utility-bill.pdf", "proof_type": "address" }'
    ```
  </Step>
</Steps>

Pass each value from `required_proof_types` as the `proof_type`. For US
entities, `required_proof_types` includes both `incorporation` and `address`
as standard requirements. `address` is **proof of physical address** at the
entity level — a utility bill, bank statement, or lease/rental agreement
evidencing the entity's relationship to its physical address. The `address`
requirement applies to US entities only; foreign entities are not asked for
it. Other common
values include `articles_of_organization`, `bylaws`, `operating_agreement`,
`ein`, `bank_statement`, and `beneficial_owner_id`.

<Note>
  Address documents uploaded for a representative do not satisfy the
  entity-level `address` requirement — the document must evidence the
  *entity's* physical address. `next_step.can_submit` stays `false` until every
  proof type in `required_proof_types` has been uploaded.
</Note>

## 6. Submit the application

Once `next_step.can_submit` is `true`, submit the application with
`POST /entities/{entity_id}/submit`. Choose the checking account product to open,
and attest that you are authorized to submit on the business's behalf. If
anything is still outstanding, this returns `400` with the remaining
requirements; check `next_step.can_submit` first.

```bash theme={null}
curl -X POST https://api.meow.com/v1/entities/$ENTITY_ID/submit \
  -H "x-api-key: $MEOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "banking_account_product_type": "grasshopper", "attestation": true }'
```

```json Response theme={null}
{
  "entity_id": "b1b2c3d4-...",
  "status": "under_review",
  "next_step": { "step": "monitor_review", "phase": "submitted", "missing": [], "required_proof_types": [], "can_submit": false }
}
```

* `banking_account_product_type` is `grasshopper` or `crb`.
* `attestation` must be `true`.

Once submitted, the application moves to review and can no longer be edited. Meow
moves `status` to `under_review`, then `approved` or `rejected`. Watch for the
outcome with webhooks (below) or by polling the status endpoint.

## Sandbox: simulate application approval

In sandbox and development environments there is no human compliance review, so
a submitted application would otherwise sit in `under_review` forever. Simulate
the approval with
`POST /simulations/entities/{entity_id}/application_approval`. It approves the
application as if compliance review had passed and kicks off checking-account
onboarding.

```bash theme={null}
curl -X POST https://api.sandbox.meow.com/v1/simulations/entities/$ENTITY_ID/application_approval \
  -H "x-api-key: $MEOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "banking_account_product_type": "grasshopper" }'
```

* The body is optional. `banking_account_product_type` defaults to
  `grasshopper`; `crb` is also supported.
* Use a **global** (user) API key with the `simulations:write` scope; the key's
  user must be an admin of the entity.
* The application must already be submitted (section 6).

After calling it, the application's `status` moves to `submitted` immediately
and to `approved` once the checking account activates. Poll
`GET /entities/{entity_id}` or subscribe to the `application.*` webhook events
to observe the transition.

<Warning>
  Simulation endpoints are only available in **sandbox and development**
  environments. In production they return `404 Not Found`.
</Warning>

## 7. Track progress to submission

`GET /entities/{entity_id}` returns the lifecycle `status`, uploaded
`documents`, and `next_step`.

```bash theme={null}
curl https://api.meow.com/v1/entities/$ENTITY_ID -H "x-api-key: $MEOW_API_KEY"
```

```json Response theme={null}
{
  "entity_id": "b1b2c3d4-...",
  "onboarding_type": "api",
  "status": "pending_user_action",
  "kyc_status": "pending",
  "business_name": "Catnip Coffee Co",
  "created_at": "2026-06-26T18:04:11.512Z",
  "updated_at": "2026-06-26T18:22:54.001Z",
  "documents": [
    { "proof_type": "incorporation", "file_name": "certificate.pdf" },
    { "proof_type": "address", "file_name": "utility-bill.pdf" }
  ],
  "next_step": { "step": "submit_application", "phase": "ready_to_submit", "missing": [], "required_proof_types": [], "can_submit": true }
}
```

| `status`                  | Meaning                                |
| ------------------------- | -------------------------------------- |
| `pending_document_upload` | No documents uploaded yet.             |
| `pending_user_action`     | Still collecting required data.        |
| `submitted`               | Submitted; queued for review.          |
| `under_review`            | Meow is reviewing the application.     |
| `approved`                | KYB approved (and an account is open). |
| `rejected`                | KYB rejected.                          |

## 8. List your entities

`GET /entities` returns every entity your key's user owns, each tagged with its
`onboarding_type`, with opaque-offset pagination.

```bash theme={null}
curl "https://api.meow.com/v1/entities?limit=20" -H "x-api-key: $MEOW_API_KEY"
```

```json Response theme={null}
{
  "entities": [
    { "entity_id": "b1b2c3d4-...", "onboarding_type": "api", "status": "under_review", "business_name": "Catnip Coffee Co", "created_at": "...", "updated_at": "..." }
  ],
  "page": { "nextOffset": "20" }
}
```

When `page` is non-null, pass `page.nextOffset` as the `offset` query parameter
for the next page. When `page` is `null`, you've reached the end.

## Track updates with webhooks

Subscribe to `identity_verification.*` for KYC outcomes and `application.*` for
KYB lifecycle changes, then update your records from the delivered payloads.
See the [webhooks guide](/guides/webhooks) for signing, retries, and
out-of-order handling.

## End to end

```bash theme={null}
# 1. Create the entity with its business details.
ENTITY_ID=$(curl -s -X POST https://api.meow.com/v1/entities \
  -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
  -d '{"business_name":"Catnip Coffee Co","business_date_of_incorporation":"2021-03-01","legal_structure":"ccorp","incorporation_state":"DE","business_address":{"address":"9 Whisker Way","city":"San Francisco","state":"CA","zip":"94105","country":"US"}}' \
  | jq -r .id)

# 2. Fill in the remaining business profile.
curl -s -X PATCH https://api.meow.com/v1/entities/$ENTITY_ID/business-details \
  -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
  -d '{"business_phone":"+14155550123","business_taxid":"12-3456789","business_website":"https://catnipcoffee.com","business_description":"Specialty coffee roaster and cafe","industry":"limited_service_restaurants","account_purpose":"business_operations","provides_blockchain_or_digital_currency_services":false,"transacts_in_digital_currency":false,"develops_governs_supports_blockchain_protocol":false}'

# 3. Add the primary representative.
REP_ID=$(curl -s -X POST https://api.meow.com/v1/entities/$ENTITY_ID/representatives \
  -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
  -d '{"email":"felix@catnipcoffee.com","title_id":"chief executive officer","is_beneficial_owner":true,"percent_ownership":"50","is_primary":true}' \
  | jq -r .representative_id)

# 4. Verify them: submit their data, or mint a self-serve link instead.
curl -s -X POST https://api.meow.com/v1/entities/$ENTITY_ID/representatives/$REP_ID/kyc \
  -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
  -d '{"first_name":"Felix","last_name":"Whiskers","date_of_birth":"1990-01-01","id_number":"123456789","id_type":"us_ssn","address":{"address":"1 Catnip Court","city":"San Francisco","state":"CA","zip":"94105","country":"US"}}'

# 5. Upload each document in next_step.required_proof_types — for a US entity,
#    "incorporation" and "address" (proof of physical address).
for DOC in "incorporation certificate.pdf" "address utility-bill.pdf"; do
  set -- $DOC
  UPLOAD=$(curl -s -X POST https://api.meow.com/v1/entities/$ENTITY_ID/documents/upload-url \
    -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
    -d "{\"proof_type\":\"$1\",\"file_name\":\"$2\",\"content_type\":\"application/pdf\"}")
  curl -s -X PUT "$(echo $UPLOAD | jq -r .upload_url)" \
    -H "Content-Type: application/pdf" --data-binary @$2
  curl -s -X POST https://api.meow.com/v1/entities/$ENTITY_ID/documents/confirm \
    -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
    -d "{\"object_key\":\"$(echo $UPLOAD | jq -r .object_key)\",\"proof_type\":\"$1\"}"
done

# 6. Poll until ready: KYC clears asynchronously and every required document
#    must be uploaded before can_submit turns true.
until [ "$(curl -s https://api.meow.com/v1/entities/$ENTITY_ID \
  -H "x-api-key: $MEOW_API_KEY" | jq -r .next_step.can_submit)" = "true" ]; do
  sleep 5
done

# 7. Submit for review.
curl -s -X POST https://api.meow.com/v1/entities/$ENTITY_ID/submit \
  -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \
  -d '{"banking_account_product_type":"grasshopper","attestation":true}' | jq .status
```

## See also

<CardGroup cols={2}>
  <Card title="Create an entity" icon="building" href="/api-reference/onboarding/create-entity">
    Full request contract for `POST /entities`.
  </Card>

  <Card title="Update business details" icon="pen" href="/api-reference/onboarding/update-business-details">
    Fill in or correct KYB details before submission.
  </Card>

  <Card title="Add a representative" icon="user-plus" href="/api-reference/onboarding/add-representative">
    Beneficial owners and officers, with the primary signer flag.
  </Card>

  <Card title="Submit the application" icon="paper-plane" href="/api-reference/onboarding/submit-entity-application">
    Choose the account product and attest, then send for review.
  </Card>
</CardGroup>
