diff --git a/mintlify/global-p2p/onboarding/configuring-customers.mdx b/mintlify/global-p2p/onboarding/configuring-customers.mdx
index c9a8fcda1..b185cb52e 100644
--- a/mintlify/global-p2p/onboarding/configuring-customers.mdx
+++ b/mintlify/global-p2p/onboarding/configuring-customers.mdx
@@ -5,6 +5,8 @@ icon: "/images/icons/people.svg"
"og:image": "/images/og/og-global-p2p.png"
---
import KycWebhooks from '/snippets/kyc/kyc-webhooks.mdx'
+import VerificationWebhooks from '/snippets/kyc/verification-webhooks.mdx'
+import CustomerDataRequirements from '/snippets/kyc/customer-data-requirements.mdx'
import CustomerTypes from '/snippets/creating-customers/customer-types.mdx'
import OnboardingModel from '/snippets/creating-customers/onboarding-model.mdx'
import KycRegulated from '/snippets/kyc/kyc-regulated.mdx'
@@ -22,16 +24,6 @@ If using sending and receiving to just-in-time UMA addresses, you'll also need t
## Creating Customers
-
-
-
-
-
-
-
-
-
-
### Individual customers
In some cases, only the above fields are required at customer creation. Beyond those base requirements, additional fields commonly associated with individual customers include:
@@ -59,6 +51,18 @@ When creating or updating customers, the `customerType` field must be specified
There can be multiple customers with the same platformCustomerId but different UMA addresses. This is useful if you want to track multiple UMA addresses and/or bank accounts for the same customer in your platform.
+## What to Collect
+
+
+
+## Regulated Platforms
+
+
+
+## Unregulated Platforms
+
+
+
## Customer Creation Process
### Creating a new individual customer (regulated institutions)
@@ -191,10 +195,14 @@ Response:
4. After the user is redirected back to your app, they can continue with account setup until KYC review is complete.
5. Track the decision via the `CUSTOMER.KYC_APPROVED` / `CUSTOMER.KYC_REJECTED` webhook (use the `CUSTOMER.KYB_*` siblings for business customers), or poll `GET /customers/{customerId}` and inspect `kycStatus`. On `APPROVED`, the customer is ready to transact and you can unlock funding.
-### Handling KYC/KYB Webhooks
+## Handling KYC/KYB Webhooks
+### Handling Verification Issues
+
+
+
## Customer management
### Retrieving customer information
diff --git a/mintlify/payouts-and-b2b/onboarding/configuring-customers.mdx b/mintlify/payouts-and-b2b/onboarding/configuring-customers.mdx
index a29ae1ec3..753a019c8 100644
--- a/mintlify/payouts-and-b2b/onboarding/configuring-customers.mdx
+++ b/mintlify/payouts-and-b2b/onboarding/configuring-customers.mdx
@@ -7,6 +7,9 @@ icon: "/images/icons/people.svg"
import KycRegulated from '/snippets/kyc/kyc-regulated.mdx'
import KycUnregulated from '/snippets/kyc/kyc-unregulated.mdx'
+import KycWebhooks from '/snippets/kyc/kyc-webhooks.mdx'
+import VerificationWebhooks from '/snippets/kyc/verification-webhooks.mdx'
+import CustomerDataRequirements from '/snippets/kyc/customer-data-requirements.mdx'
This guide provides comprehensive information about customer configuration in the Grid API, including customer types, registration processes, management, and bank account information.
@@ -19,18 +22,27 @@ Your platform's configuration (retrieved via `GET /config`) includes a `supporte
## Customer Registration Process
-### Creating a New Customer
-
When creating or updating customers, the `customerType` field must be specified as either `INDIVIDUAL` or `BUSINESS`. Depending if you are a regulated or unregulated platform your KYC/KYB requirements will vary.
-
-
-
-
-
-
-
-
+## What to Collect
+
+
+
+## Regulated Platforms
+
+
+
+## Unregulated Platforms
+
+
+
+## Handling KYC/KYB Webhooks
+
+
+
+### Handling Verification Issues
+
+
## Customer Management
diff --git a/mintlify/ramps/onboarding/configuring-customers.mdx b/mintlify/ramps/onboarding/configuring-customers.mdx
index 5e5c016d3..0ad336bc4 100644
--- a/mintlify/ramps/onboarding/configuring-customers.mdx
+++ b/mintlify/ramps/onboarding/configuring-customers.mdx
@@ -8,23 +8,32 @@ icon: "/images/icons/people.svg"
import KycRegulated from '/snippets/kyc/kyc-regulated.mdx'
import KycUnregulated from '/snippets/kyc/kyc-unregulated.mdx'
import KycWebhooks from '/snippets/kyc/kyc-webhooks.mdx'
+import VerificationWebhooks from '/snippets/kyc/verification-webhooks.mdx'
+import CustomerDataRequirements from '/snippets/kyc/customer-data-requirements.mdx'
import { FeatureCard, FeatureCardGrid } from '/snippets/feature-card.mdx'
Customers must complete identity verification before processing conversions. The required information varies based on your platform's regulatory status.
-
-
-
-
-
-
-
-
+## What to collect
+
+
+
+## Regulated platforms
+
+
+
+## Unregulated platforms
+
+
## Monitor verification status
+### Handling verification issues
+
+
+
Only customers with `APPROVED` status can create quotes and process
conversions.
diff --git a/mintlify/rewards/onboarding/configuring-customers.mdx b/mintlify/rewards/onboarding/configuring-customers.mdx
index 10da95874..7fb9ee308 100644
--- a/mintlify/rewards/onboarding/configuring-customers.mdx
+++ b/mintlify/rewards/onboarding/configuring-customers.mdx
@@ -8,6 +8,8 @@ icon: "/images/icons/people.svg"
import KycRegulated from '/snippets/kyc/kyc-regulated.mdx'
import KycUnregulated from '/snippets/kyc/kyc-unregulated.mdx'
import KycWebhooks from '/snippets/kyc/kyc-webhooks.mdx'
+import VerificationWebhooks from '/snippets/kyc/verification-webhooks.mdx'
+import CustomerDataRequirements from '/snippets/kyc/customer-data-requirements.mdx'
This guide covers everything you need to know about creating and managing customers in the Grid API for Bitcoin rewards distribution.
@@ -27,23 +29,30 @@ The `platformCustomerId` field is optional but recommended. Use your existing us
## Customer Onboarding
-
-
-
-
-
-
-
-
-
When a customer is created successfully, internal accounts are automatically created for each currency configured on your platform. These accounts can be used as sources or destinations for transfers.
-### Handling KYC/KYB Webhooks
+## What to Collect
+
+
+
+## Regulated Platforms
+
+
+
+## Unregulated Platforms
+
+
+
+## Handling KYC/KYB Webhooks
+### Handling Verification Issues
+
+
+
## Listing Customers
Retrieve a paginated list of customers with optional filtering:
diff --git a/mintlify/snippets/kyc/customer-data-requirements.mdx b/mintlify/snippets/kyc/customer-data-requirements.mdx
new file mode 100644
index 000000000..ec8b83ed6
--- /dev/null
+++ b/mintlify/snippets/kyc/customer-data-requirements.mdx
@@ -0,0 +1,31 @@
+import KybDataRequirements from '/snippets/kyc/kyb-data-requirements.mdx'
+
+What you collect depends on the customer type: **KYC** for `INDIVIDUAL` customers, **KYB** for `BUSINESS` customers. Grid validates against these requirements when you call `POST /verifications` — anything missing comes back as a `RESOLVE_ERRORS` entry naming the exact field or document.
+
+
+Most of these fields are optional to the API schema at customer creation and only become mandatory at verification. Two things widen the list further: the `providerRequiredCustomerFields` for each currency in your platform's configuration (`GET /config`), and the KYC provider's per-jurisdiction rules. Treat the lists below as the baseline, not the ceiling.
+
+
+### KYC — individual customers
+
+Personal information, submitted via `POST /customers` (or `PATCH /customers/{customerId}`):
+
+| Field | Notes |
+|-------|-------|
+| `fullName` | Must contain both a given name and a family name — a single-word name is rejected at submission |
+| `birthDate` | `YYYY-MM-DD` |
+| `nationality` | ISO 3166-1 alpha-2 |
+| `address` | `line1`, `postalCode`, and `country` are required; `line2`, `city`, and `state` as applicable |
+| `idType` + `identifier` | Required to onboard a US account holder. Only `SSN` (format `###-##-####`) and `ITIN` are accepted for individuals |
+| `email` / `phoneNumber` | Required in regions that verify a contact channel before verification begins (for example, the EU) |
+
+Documents, uploaded via `POST /documents` — identity documents also require `documentNumber` and `issuingAuthority`:
+
+- **Identity document** — a `PASSPORT` (single image), or a `DRIVERS_LICENSE` / `NATIONAL_ID` uploaded as separate `FRONT` and `BACK` images via the `side` field.
+- **Proof of address** — requested in some jurisdictions and for some currencies. Any of `UTILITY_BILL`, `ELECTRICITY_BILL`, `RENT_OR_LEASE_AGREEMENT`, `BANK_STATEMENT`, or `TAX_RETURN`, dated within the last 3 months.
+
+Some jurisdictions, flows, and volume tiers also require enhanced due diligence. These are optional top-level fields on the individual customer — `sourceOfFundsCategories`, `sourceOfWealthCategories`, `purposeOfAccount`, `expectedMonthlyTransactionCount`, `expectedMonthlyTransactionVolume`, `annualIncomeRange`, `netWorthRange`, and `pepStatus`. The provider decides which apply; you learn which ones through `MISSING_FIELD` errors on `POST /verifications` rather than up front. Each `*Categories` field that includes `OTHER` also requires its matching `*OtherDescription`.
+
+### KYB — business customers
+
+
diff --git a/mintlify/snippets/kyc/kyc-regulated.mdx b/mintlify/snippets/kyc/kyc-regulated.mdx
index 69cd0687e..b2697b9d4 100644
--- a/mintlify/snippets/kyc/kyc-regulated.mdx
+++ b/mintlify/snippets/kyc/kyc-regulated.mdx
@@ -11,7 +11,7 @@ Regulated financial institutions can:
- **Reduced Documentation**: Only provide essential customer information required by your payment counterparty or service provider.
- **Faster Onboarding**: Streamlined process for known, verified customers
-#### Creating Customers via Direct API
+### Creating customers via direct API
For regulated platforms, you can create customers directly through the API without requiring external KYC verification:
diff --git a/mintlify/snippets/kyc/verification-webhooks.mdx b/mintlify/snippets/kyc/verification-webhooks.mdx
new file mode 100644
index 000000000..6ff36563a
--- /dev/null
+++ b/mintlify/snippets/kyc/verification-webhooks.mdx
@@ -0,0 +1,138 @@
+The `CUSTOMER.KYC_*` and `CUSTOMER.KYB_*` events above tell you the decision. The `VERIFICATION.*` events tell you **why a verification is stuck and what to send to unstick it** — they carry the full `Verification` resource, including the `errors` array naming each unmet requirement.
+
+| Event | Meaning | What to do |
+|-------|---------|------------|
+| `VERIFICATION.RESOLVE_ERRORS` | Blocked on missing, invalid, or rejected data | Resolve every entry in `errors`, then re-submit |
+| `VERIFICATION.PENDING_MANUAL_REVIEW` | A reviewer is assessing the case | Nothing — wait for the terminal event |
+| `VERIFICATION.IN_PROGRESS` | Submitted; provider checks are running | Nothing — wait |
+| `VERIFICATION.APPROVED` | Verification passed | Unlock funding and money movement |
+| `VERIFICATION.REJECTED` | Verification failed | Surface the outcome; see the non-recoverable errors below |
+
+
+These fire for both individuals and businesses — the customer type is determined by the customer referenced in `data.customerId`. They are most useful for direct API onboarding, where your integration is responsible for collecting what's missing. In the hosted flow the applicant resolves most issues inside the flow itself, so integrate against `kycStatus` / `kybStatus` there.
+
+
+**Example payload:**
+
+```json
+{
+ "id": "Webhook:019542f5-b3e7-1d02-0000-000000000031",
+ "type": "VERIFICATION.RESOLVE_ERRORS",
+ "timestamp": "2025-08-15T14:32:00Z",
+ "data": {
+ "id": "Verification:019542f5-b3e7-1d02-0000-000000000011",
+ "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
+ "verificationStatus": "RESOLVE_ERRORS",
+ "errors": [
+ {
+ "resourceId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
+ "type": "MISSING_PROOF_OF_ADDRESS_DOCUMENT",
+ "acceptedDocumentTypes": ["UTILITY_BILL", "BANK_STATEMENT"],
+ "reason": "Proof of address document is required"
+ }
+ ],
+ "createdAt": "2025-08-15T14:00:00Z"
+ }
+}
+```
+
+### Reading an error
+
+
+The resource to fix — a `Customer`, a `BeneficialOwner`, or a `Document`. On a business verification this is what tells you *whose* data is incomplete, since one verification covers the company and every owner.
+
+
+
+The error category. Drives what you do next — see the remediation table below.
+
+
+
+Dot-notation path to the offending field (for example, `customer.address.line1`). Present for `MISSING_FIELD` and `INVALID_FIELD`.
+
+
+
+The document types that would satisfy the requirement — upload any one of them. Present on the document-related error types.
+
+
+
+Human-readable description of the issue. Safe to log; write your own customer-facing copy rather than surfacing this text directly.
+
+
+### Resolving each error type
+
+| Error types | How to resolve |
+|-------------|----------------|
+| `MISSING_FIELD`, `INVALID_FIELD` | `PATCH` the resource named by `resourceId` (`/customers/{id}` or `/beneficial-owners/{id}`), setting the path given in `field` |
+| `MISSING_LEGAL_PRESENCE_DOCUMENT`, `MISSING_CONTROL_STRUCTURE_DOCUMENT`, `MISSING_OWNERSHIP_STRUCTURE_DOCUMENT`, `MISSING_PROOF_OF_ADDRESS_DOCUMENT`, `MISSING_IDENTITY_DOCUMENT`, `MISSING_GOOD_STANDING_DOCUMENT` | `POST /documents` with any type listed in `acceptedDocumentTypes` |
+| `INVALID_DOCUMENT`, `EXPIRED_DOCUMENT`, `POOR_QUALITY_DOCUMENT`, `UNREADABLE_DOCUMENT`, `INCOMPLETE_DOCUMENT`, `WRONG_DOCUMENT_TYPE`, `DOCUMENT_VERIFICATION_FAILED` | Collect a replacement — current, legible, complete, and of an accepted type — and upload it with `POST /documents` |
+| `MISSING_BENEFICIAL_OWNER`, `MISSING_CONTROL_PERSON` | `POST /beneficial-owners` for the missing individual. Every owner at 25% or more, plus one control person, must be registered |
+| `SUSPECTED_FRAUD_DOCUMENT`, `APPLICANT_SANCTIONED`, `APPLICANT_FRAUD`, `APPLICANT_CRIMINAL_RECORD`, `APPLICANT_REJECTED` | Not resolvable by re-submitting. Route these to your compliance process |
+
+
+The `APPLICANT_*` types and `SUSPECTED_FRAUD_DOCUMENT` are compliance outcomes, not data problems. Re-submitting the same customer will not clear them, and retry loops on these are counterproductive — handle them as a terminal outcome for that customer.
+
+
+### Re-submitting
+
+Once every recoverable error is resolved, call `POST /verifications` again with the same `customerId`. The response carries the updated `verificationStatus` — `RESOLVE_ERRORS` again if anything is still outstanding, so you can iterate without waiting for another webhook. To re-read the state at any point, call `GET /verifications?customerId={customerId}`.
+
+
+Once a customer reaches `APPROVED` or `REJECTED`, further `POST /verifications` calls return `400`.
+
+
+
+```javascript
+// Handler for VERIFICATION.* events. RESOLVE_ERRORS is the one that needs work:
+// every entry in data.errors names a resource, a category, and often the exact
+// field or the document types that would satisfy it.
+const NON_RECOVERABLE = new Set([
+ 'SUSPECTED_FRAUD_DOCUMENT',
+ 'APPLICANT_SANCTIONED',
+ 'APPLICANT_FRAUD',
+ 'APPLICANT_CRIMINAL_RECORD',
+ 'APPLICANT_REJECTED',
+]);
+
+app.post('/webhooks/verification-status', async (req, res) => {
+ const { id, type, data } = req.body;
+
+ // The webhook id is unique per delivery — use it to drop duplicates.
+ if (await alreadyProcessed(id)) {
+ return res.status(200).send('OK');
+ }
+
+ switch (type) {
+ case 'VERIFICATION.RESOLVE_ERRORS': {
+ const blocking = data.errors.filter((e) => NON_RECOVERABLE.has(e.type));
+ if (blocking.length > 0) {
+ await escalateToCompliance(data.customerId, blocking);
+ break;
+ }
+ // Ask the customer for exactly what's outstanding, then re-submit
+ // with POST /verifications once it's all supplied.
+ await requestOutstandingItems(data.customerId, data.errors);
+ break;
+ }
+
+ case 'VERIFICATION.PENDING_MANUAL_REVIEW':
+ case 'VERIFICATION.IN_PROGRESS':
+ await markCustomerUnderReview(data.customerId);
+ break;
+
+ case 'VERIFICATION.APPROVED':
+ await activateCustomer(data.customerId);
+ break;
+
+ case 'VERIFICATION.REJECTED':
+ await notifySupport(data.customerId, 'VERIFICATION_REJECTED');
+ break;
+
+ default:
+ console.log(`Unexpected webhook type ${type} for ${data.customerId}`);
+ }
+
+ await recordProcessed(id);
+ res.status(200).send('OK');
+});
+```
+