This guide walks through onboarding aDocumentation Index
Fetch the complete documentation index at: https://docs.business.blaaiz.com/llms.txt
Use this file to discover all available pages before exploring further.
type=business customer from creation to verification. Read Business customer KYB first if you haven’t — it explains the lifecycle, the kyb_scope model (Standard / FULL vs Minimal), and the rules that this walkthrough assumes.
The first six steps cover Standard (FULL) onboarding — the default flow, which produces a customer that can hold an NGN, USD, GBP, or EUR Virtual Bank Account. The Minimal KYB onboarding section at the end is the abbreviated NGN-only flow for whitelisted merchants. If you’re not sure which one applies to you: you almost certainly want Standard.
Prerequisites
- An OAuth access token with
customer:writeandcustomer:readscopes — see Authentication. - The business’s basic profile information (name, registration number, country of incorporation, etc.).
- Each beneficial owner’s personal details and a clear ID document image.
- The company’s formation document (Certificate of Incorporation or equivalent).
Step 1: Create the customer
Create the customer withtype=business. Beneficial owners are required for verification (see Readiness) but you don’t have to include them on this initial call — you can attach them now via the owners array (up to 5 entries) or add them later via PUT /api/external/customer/{id}. Either way, every owner that exists at /submit time must satisfy the readiness checks.
For business customers the canonical identifier is
registration_number + incorporation_country, not the legacy id_type / id_number fields. Both registration_number and incorporation_country are required at create time. The actual certificate-of-incorporation file goes into the KYB document collection in step 4 — never through /files.Two countries, two addresses. A business customer carries up to three ISO country codes:incorporation_country— the legal jurisdiction the company is registered in. Top-level, business-only, required.country— the registered-address country. Top-level. For businesses, must equalincorporation_country(a company’s registered office sits in its country of incorporation by company law). The cross-field check is enforced at validation time — sending divergent values fails the request with422. When changing the jurisdiction on update, send both fields together.operating_country— the operating-address country, can differ from the other two. Lives on KYC data alongside the rest of theoperating_*fields. Required whenever any otheroperating_*field is supplied (so each address is self-describing).
zip_code against country, operating_zip_code against operating_country.Do not send id_type or id_number on a business customer. Those columns are individual-only — they identify a person’s passport / driver’s license / resident permit. Businesses identify via registration_number + incorporation_country. Sending id_type or id_number on a business create or update returns 422.id as your customer_id. The verification_status is PENDING.
Step 2: Add KYC data (operating address, etc.)
If you have additional address or KYC fields to capture — including the operating address when it differs from the registered address — callPOST /api/external/customer/{id}/kyc-data:
operating_country anchors the postal-code validation on operating_zip_code — if you send any of the operating_* fields and operating_country is not yet persisted on the row, you must include it.
This step is optional — fields you didn’t capture at creation can also be patched via PUT /api/external/customer/{id} later.
Step 3: Upload an ID file for each owner
Each owner needs at least theirid_document_front. For two-sided IDs (e.g. driver’s license) include id_document_back. The flow is two steps per file: get a presigned URL, PUT the file to S3, then register the upload.
3.1 Get a presigned URL
file_id, url (a 5-minute presigned S3 URL), and headers.
3.2 PUT the file to S3
application/pdf, image/jpeg, image/png. The file must be clear, legible, and authentic — see General guidelines for the document quality rules. Fraudulent documents result in permanent blacklisting.
3.3 Register the upload
file_category=id_document_back round.
Step 4: Register KYB documents
The company’s formation document and any other corporate paperwork (articles, share register, bank statements, proof of address, etc.) go into the KYB document collection. Same two-step pattern.4.1 Get a presigned URL
file_id and a 5-minute url.
4.2 PUT the file to S3
Same as 3.2.4.3 Register the document
/submit will accept the customer. Formation types are CERTIFICATE_OF_INCORPORATION, ARTICLES_OF_INCORPORATION, BENEFICIAL_OWNERSHIP_CERTIFICATE, INCORPORATION_DOCUMENTS, and CAC_STATUS_REPORT. Compliance may require additional document types depending on the business profile — review feedback comes through admin_comments on the customer-level rejection.
Step 5: Submit for verification
PROCESSING. All PENDING owners and documents flip to PROCESSING along with it. The customer-level fields, every owner, and every document are now locked from your edits until the customer-level webhook arrives.
If the readiness check fails, the response is 422 with a message naming the specific gap. The full list of preconditions is in Readiness. Common cases you’ll hit:
| Symptom | Where to fix it |
|---|---|
| ”at least one owner is required” | Add an owner (step 1 or PUT /customer/{id} with the owners array). |
| “ownership_percentage of all owners must sum to 100%“ | Adjust the percentages across all owners so they total 100. |
| ”at least one formation document is required” | Register one with a formation type — step 4. |
| ”owner … is missing id_document_front” | Run step 3 for that owner. |
| ”owner … id_document_type=drivers_license requires id_document_back” | Add the back-side upload — re-run step 3 with file_category=id_document_back. |
| ”owner … id_document_type=passport must not have id_document_back” | The owner has a stale back image from a previous id_document_type. Either change the type back to one that requires it, or contact support to clear the file. |
| ”owner … date_of_birth must be at least 18 years ago” | Update the owner’s DOB. |
| ”owner … id_expiry_date must be a future date” | Replace the ID with a current document and update id_expiry_date. |
| ”owner emails must be unique within the customer” | Two owners share an email. Update one of them. |
| ”business_name / registration_number / incorporation_country is required” | The field was cleared since creation. Set it via PUT /customer/{id}. |
| ”incorporation_country must be a valid ISO 3166-1 alpha-2 country code” | Use a real ISO code (e.g. NG, US, GB). |
| “country must equal incorporation_country” | The two diverged. Update both together via PUT /customer/{id} — {"country": "GB", "incorporation_country": "GB"} — so the registered-address country matches the legal jurisdiction. |
Step 6: Wait for the webhook
You will receive acustomer.status_changed webhook on your collection_url when the customer transitions to VERIFIED or REJECTED. Business KYB review typically takes 1–5 business days. Do not poll, do not raise tickets within that window — see General guidelines.
If VERIFIED, the customer is ready to use — for an NGN Virtual Bank Account, an NGN payout, or any other operation that requires a verified customer.
If REJECTED, every owner and document that needed fixing will have entries in its admin_comments array explaining what’s wrong. See Handling KYB rejections.
Common mistakes
These are the patterns we see most often that cause grief. 1. CallingPOST /customer/{id}/files (or POST /file/get-presigned-url) for a business customer.
Both endpoints reject business customers with 400 — they’re individual-only. Symptom: you can’t even upload. Fix: use the dedicated owner-file and KYB-document endpoints from steps 3 and 4.
2. Ownership percentages that don’t sum to 100.
/submit enforces this strictly. 99.99% won’t do. Add up the values across every owner and make sure they total exactly 100.
3. Trying to edit a customer (or its owners / documents) while the customer is PROCESSING or VERIFIED.
The API returns 400. PROCESSING is the active-review window — wait for the customer-level webhook before attempting any edits. If the customer comes back REJECTED, parent + child edits unlock and you can fix the issues. For corrections on a VERIFIED customer, see Customer data corrections.
4. Sending status or admin_comments in your update payloads.
Those fields are admin-managed. They’re silently stripped from your requests — your owner stays at PENDING and your comments don’t get saved. Don’t rely on them appearing on the response if you sent them.
5. Forgetting registration_number and incorporation_country at create time.
These two are required for type=business and they’re how the business is indexed in our compliance system. The legacy id_type / id_number are prohibited on business customers — sending either returns 422.
6. Sending incorporation_country without country (or vice versa) on update.
For business customers, country (registered-address country) must equal incorporation_country (legal jurisdiction). When you change the jurisdiction, update both fields in the same PUT payload. Updating only one leaves the other at its persisted value, which produces a divergent pair and a 422. Sending different values for the two also returns 422.
7. Sending operating_* fields without operating_country.
Each address has its own country anchor: zip_code validates against country, operating_zip_code validates against operating_country. At create time, sending any operating_* field requires operating_country. At update time, the persisted value is used as a fallback — but if you’ve never set it, the request fails.
8. Treating customer.created as “ready to transact.”
customer.created fires the moment you POST /customer. Verification has not even started yet. Wait for customer.status_changed with new_status=VERIFIED.
Minimal KYB onboarding
This section only applies if your platform has been placed on the Minimal KYB allow list by Blaaiz. If you haven’t been told you’re on the allow list, you aren’t — sending
kyb_scope=MINIMAL returns 422 with a “not configured for minimal KYB” message. Read Two onboarding paths for context.Step 1 (Minimal): Create the customer
type=businesskyb_scope=MINIMAL(or omit it — allow-listed platforms default to MINIMAL)business_nameemailcountry(ISO alpha-2)incorporation_country(ISO alpha-2; must equalcountry)registration_number— the company’s registration / RC number
- Beneficial owners (owners array)
- Per-owner ID files
- Operating address
id as your customer_id.
Step 2 (Minimal): Register the formation document
This step is mandatory and identical to Step 4 of the Standard flow: get a presigned URL, PUT the file to S3, then register it viaPOST /customer/{id}/document with type=CERTIFICATE_OF_INCORPORATION.
Step 3 (Minimal): Submit for verification
auto_verify_business_customers enabled, the customer goes straight to VERIFIED. Otherwise it goes to PROCESSING and compliance reviews it (typically within 1 business day for the Minimal data set, since there’s less to review).
You’re done — the customer can now have an NGN VBA created.
Upgrading from Minimal to Full
If a Minimal customer later needs to hold a USD / GBP / EUR VBA, you upgrade them to Full by adding beneficial-owner data:- Saves the new owners on the customer record.
- Flips
kyb_scopetoFULL. - If the customer was
VERIFIED, transitions them back toPENDING.
registration_number and incorporation_country are already on the customer from Minimal onboarding, so you don’t need to send them again. (You can if you want to update them, but typically you don’t.)
After the upgrade, upload owner ID files via the normal owner-file endpoints (Step 3 of the Standard flow), and call /submit to re-verify under the Standard checks.
One-way only — there is no downgrade. If you’re not sure whether to onboard a customer at Minimal or Full, default to Full (or whichever scope your default behavior produces): you can always add owners and re-submit, but you can’t remove the Full data once it’s on file.
