Skip to main content
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.
This is self-serve onboarding for entities your key owns. The key’s user is the creator and admin. The user is not added as a representative automatically. If this user is the business signer, add the user as the primary representative in step 3. To onboard entities on behalf of your customers as a platform, use Partner Onboarding instead.

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.
Testing through the development API. The examples below use https://api.dev.meow.com/v1. This environment also exposes the approval simulation that stands in for compliance review, so you can drive an application all the way to approved and open a simulated checking account. Non-production keys are granted the simulations:write scope automatically.
For development testing, use synthetic values like these throughout the flow: Every onboarding read returns a next_step. For a US entity mid-onboarding it looks like this:
The loop: call an endpoint, then GET /entities/{entity_id}, act on next_step.step, repeat until can_submit is true, then submit.
Some entities draw an extra due-diligence step (complete_due_diligence) before they can submit. Forms that collect plain data are submittable through the due-diligence endpoints; forms that require document uploads must be finished in the Meow dashboard. A follow-up information request after submission (resolve_info_requests) is answered through the API. Read next_step.info_requests[] for the request_id, then fetch the form, upload each document and answer the questions.

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.
Response
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.
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.

International businesses

Meow onboards businesses in 44 countries, not just the US. The flow is identical to the walkthrough here; only a few fields differ for a business incorporated outside the US.
Supported countries (ISO 3166-1 alpha-2): AD, AT, AU, BE, BM, BS, CA, CH, CL, CZ, DE, DK, ES, FR, GB, GG, GR, HR, IE, IS, IT, JE, JP, KR, LT, LU, MC, MU, NL, NO, NZ, OM, PL, PR, PT, SE, SG, SI, SK, SM, TW, US, UY, VG. A business_address.country outside this set is rejected at write time with a 400 that lists every supported country.
What changes for a non-US business:
  • incorporation_state: omit it. It’s the US state of incorporation and applies to US entities only.
  • business_taxid: send the local registration or tax number instead of an EIN (a UEN in Singapore, a UTR in the UK, a BN in Canada, and so on).
  • legal_structure: use foreign_entity.
  • business_address.country: the ISO code of a supported country. A state or province and a postal code are still required where the country uses them.
A Singapore business, created in one call:
Two more differences appear later in the flow:
  • Documents: next_step.required_proof_types for a non-US business is typically ["incorporation", "shareholder_registry", "officer_registry"]: the certificate of incorporation, the shareholder registry, and the register of directors. These apply to every non-US country. The US-only address proof is not requested.
  • Representative KYC: pass each representative’s country-specific id_type and id_number (for example sg_nric, au_passport, ca_sin) instead of us_ssn. See Verify each representative.
  • Submission: choose grasshopper as the banking_account_product_type. Grasshopper Bank is Meow’s preferred checking-account provider. crb is available only to US entities. Every other supported country uses grasshopper.
Everything else, including the next_step loop, representatives, documents and submission, works exactly as described below.

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).
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. To promote a representative already on file, call POST /entities/{entity_id}/representatives/{representative_id}/make-primary. It makes that representative the signer and demotes the current one. Allowed until the application is submitted.
Response
  • title_id is one of: chief executive officer, chief financial officer, chief operating officer, chief administrative 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.
Submit the representative’s identity data and Meow runs verification on it.
Response
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.ip is required: the end user’s IPv4 or IPv6 address, forwarded to the verification provider as a fraud signal.
Sandbox testing. In sandbox and dev the verification provider only clears a known test identity, so use Leslie Knope below. The reliable path is the self-verify link (the “They self-verify” tab): mint the link and complete the flow with these exact details. A representative’s kyc_status must reach approved before the application can be approved. Poll the representatives array to confirm.
Verification completes asynchronously. next_step advances once every representative has started verification, not once they pass, so track the actual outcome per representative via their kyc_status in the representatives array on the onboarding status, or the identity_verification.* webhooks. A representative’s kyc_status matches the webhook stream; action_required means that person needs another step (for example, a document). Mint a verification link and share it so they can finish. The top-level kyc_status reflects the primary representative.

5. Upload supporting documents

next_step.required_proof_types lists exactly which documents the entity still owes. For a US entity that is ["incorporation"]. Each one uses a three-step flow: get a pre-signed URL, PUT the bytes, then confirm. Repeat the flow for each proof type:
1

Request an upload URL

Response
2

PUT the file to upload_url

Use the same Content-Type you requested, keep the file under max_bytes, and upload before the URL expires.
3

Confirm the upload

Response
4

Repeat for the proof of physical address

Pass each value from required_proof_types as the proof_type. Before submission a US entity is asked for incorporation. A reviewer can ask for more after submission, and address is the common one: it is proof of physical address at the entity level, such as a utility bill, bank statement, or lease agreement evidencing the entity’s relationship to its physical address. Those arrive as an information request, which next_step.info_requests[] reports with the request_id to upload against. Other common values include articles_of_organization, bylaws, operating_agreement, ein, bank_statement, and beneficial_owner_id.

Documents for international businesses

A non-US entity’s required_proof_types usually includes two registry documents alongside incorporation. These prove who owns and who runs the company, the things US entities establish through their EIN and representative data instead. Whatever the country, upload only the values that appear in next_step.required_proof_types. That list is the source of truth for what a given entity owes. Meow may request further documents during review; those arrive as their own requirements.
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.

Complete required due diligence

Some industries draw an extra enhanced due diligence (EDD) step before the application can be submitted. When one applies, next_step.step is complete_due_diligence. List the required forms, then for each form that’s submittable through the API, fetch its schema and submit it. Forms that collect document uploads can’t be submitted through the API (requires_webapp: true) and must be completed in the Meow dashboard.
1

List the required forms

GET /entities/{entity_id}/due-diligence returns each required form with its completion status and whether it must be finished in the web app.
Response
2

Fetch a submittable form's schema

GET /entities/{entity_id}/due-diligence/schema/{form_type} returns the form’s pages and fields, including types, choices, validation, and conditional visibility, so you can build a valid submission. Only forms submittable through the API have a schema.
3

Submit the completed form

POST /entities/{entity_id}/due-diligence submits one form. Set form_type to the form you’re answering; form_data matches that form’s schema. The response is the refreshed onboarding status.
Repeat for each submittable form. Once every required form is complete, and any requires_webapp forms are finished in the dashboard, next_step advances past complete_due_diligence and can_submit can turn true.

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. If anything is still outstanding, this returns 400 with the remaining requirements; check next_step.can_submit first. Use grasshopper for the preferred Grasshopper Bank checking account. Add stablecoins to product_features when the business also needs USDC access. Omit product_features when the business needs checking only.
Response
Neither holds up the review. missing lists everything outstanding across both, and info_requests[] carries the per-request detail. Poll this endpoint, or subscribe to the info_request.* webhooks, and act on each as it appears.

The agreements

Outstanding agreements appear in missing, and consent_url is where the primary representative accepts them. Share the link. Anyone who holds it can accept for the business, so treat it as a secret. consent on the same response gives the detail: remaining_items is what is left, and status moves pendingviewedcompleted. Submitting emits onboarding_consent.updated with status: "pending" and the full remaining_items, so a caller watching the event stream is told the agreements are owed without polling. Later events fire as the representative opens the form and accepts each item. While any agreement is outstanding, POST /accounts returns 403. The review still runs. Once the representative accepts everything, they leave missing. Requested products also wait for their agreements. Before you simulate approval, open consent_url. Then wait until consent.status is completed. If you skip this step, checking can open while a requested product remains pending.

Information requests

Each entry in next_step.info_requests[] is a reviewer asking for something. note carries their message. One request can ask for documents, questions, or both, and one schema describes all of it.
1

Fetch the form

Response
A file_upload element is a document. Its file_upload_config.proof_type is the value to pass to the upload endpoints, and accepted_types is what the file may be. Every other element is a question.
2

Upload each document

Use the request-scoped upload endpoints. The pre-submission /documents/upload-url pair is closed once you submit, and it cannot attribute a file to a request.
Confirm response
request_completed is false because the questions are still open.
3

Answer the questions

Post the answers keyed by the element name from the schema. This submits them, so send them complete. There is no draft state on this API.
Response
Once a request is satisfied, it leaves next_step.info_requests[]. With nothing else outstanding, step returns to monitor_review.
A request can expire. next_step.info_requests[] carries expires_at alongside request_id, required_proof_types, questions_pending, and the reviewer’s note. Uploads against an expired or completed request are rejected, and the request stops appearing in the list.

Reaching approved

status leaves under_review when the bank finishes, not when you clear these:
  • approved: the business application is approved and its checking account is active.
  • submitted: KYB passed and the account is still opening.
  • rejected: the application was declined.
Watch for the outcome with the application.* webhooks, or by polling GET /entities/{entity_id}. approved does not mean the queue is empty. Approval does not wait on the agreements, so an approved business can still owe them, and an information request raised before approval stays open after it. approved also does not mean that every requested product is ready. Stablecoin access has separate approval and account-creation steps. Do not run the stablecoin funding simulation until the stablecoin financial account exists. next_step.step keeps naming the work while any is left, so it never contradicts missing: an approved business that still owes its agreements reads accept_terms, and one with an open request reads resolve_info_requests. step becomes onboarding_complete only once missing and info_requests[] are both empty. A rejected application owes nothing, so it reports onboarding_complete immediately.

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. It also approves each requested product feature and starts checking-account onboarding.
  • Every representative’s kyc_status must be approved first. Real review never approves an application whose beneficial owner failed KYC, and neither does the simulation: it returns 400 and names how many representatives still need to pass. Poll GET /entities/{entity_id} and confirm every entry in the representatives array shows kyc_status: "approved" before calling this. In sandbox, get there with the verification link and the test identity.
  • The application must already be submitted (section 6).
  • Complete the agreements at consent_url before this call when the application requests another product. Otherwise, checking can open while that product remains pending.
  • The body is optional. banking_account_product_type defaults to grasshopper, Meow’s preferred provider. crb is also supported for eligible businesses.
  • Use an entity API key scoped to the target entity, or a global (user) API key with the simulations:write scope (granted automatically on non-production keys); for the global key, the key’s user must be an admin of the entity.
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. Requested stablecoin onboarding continues asynchronously after the Grasshopper account opens. The simulation approves the request, but it does not make the stablecoin financial account available in the same response. The call is safe to repeat. If account opening stalls in the sandbox (the status sits at submitted for more than a few minutes), call it again. A retry completes the stalled opening and adopts the account the sandbox bank already created, without opening a duplicate.
Simulation endpoints are only available in sandbox and development environments. In production they return 404 Not Found.

7. Track progress to submission

GET /entities/{entity_id} returns the lifecycle status, uploaded documents, next_step, and a representatives array with each representative’s kyc_status. The top-level kyc_status reflects the primary representative.
Response

8. List your entities

GET /entities returns every entity your key’s user owns, each tagged with its onboarding_type, with opaque-offset pagination.
Response
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

Instead of polling GET /entities/{entity_id}, subscribe once and let Meow push each change. A global key’s own onboarding events, identity_verification.* for KYC outcomes and application.* for KYB lifecycle changes, deliver to a regular webhook subscription, so the same key you onboard with can receive them.
Save the signing_secret from the response. It is returned once. Omit event_types (or send null) to receive every event the key can see. Each delivery carries the same status values you would otherwise poll for, so you can drive the whole flow off the stream. The key needs the webhooks:write and webhooks:read scopes, and the url must be public HTTPS. See the webhooks guide for signature verification, retries, and out-of-order handling.

End to end

See also

Create an entity

Full request contract for POST /entities.

Update business details

Fill in or correct KYB details before submission.

Add a representative

Beneficial owners and officers, with the primary signer flag.

Submit the application

Choose the account product, then send for review.