New CBN rules make manual AML non-compliant.

Read the brief
MaiGuard

Implementation Guide

Customer Onboarding & Integration

Implement customer profiles, list-driven controls, and identity alignment. Transaction scoring lives in the API reference — this guide focuses on onboarding.

How this connects to scoring

Onboarding does not replace scoring — it enriches it. When you call POST /v1/transactions/score, send the same stable userId you used as customerId here so rules can use KYC, PEP, and list context.

Tip

Use the same stable identifier as both customerId (onboarding) and userId (scoring). Mismatched IDs mean MaiGuard cannot correlate customer context with transaction risk signals.

Base URL & auth

Base URL: https://api.maiguard.com

API key

Authorization: Bearer pk_live_<key>

JWT

Authorization: Bearer <access_token>

Note

Use your pk_test_ key for integration; switch to pk_live_ at go-live.

Core sequence

  1. 1Onboard or update each customer with PUT /v1/customers/:customerId/onboard.
  2. 2Use the same customerId as userId when scoring transactions.
  3. 3Maintain tenant lists and entries for jurisdictions, PEP-style lists, or custom watchlists.
  4. 4List and verify customers via GET /v1/customers as needed.

Risk factors to collect

MaiGuard scores each customer on five risk dimensions. Two of them — business/sector and product/channel — come entirely from what you collect on your account-opening form and send at onboarding. Screening, geography, and transaction behaviour are observed by the platform; nobody has to ask for those.

FieldAsk on your formFeeds
businessSectorWhat does the business do? (e.g. retail, bureau de change, crypto)Business / sector risk
businessTypeLegal form / business type (e.g. sole trader, limited company)Business / sector risk
occupationIndividual's occupationBusiness / sector risk
productTypeWhich products will they use? (e.g. wallet, remittance, savings)Product / channel risk
deliveryChannelHow are they served? (e.g. mobile, agent, branch)Product / channel risk
metadata.sectorRiskLevelYour own classification (LOW / MEDIUM / HIGH / CRITICAL) — optionalA floor on sector risk
metadata.productRiskLevel / channelRiskLevelYour own classification — optionalA floor on product/channel risk

Tip

A risk level you declare is a floor — MaiGuard's own sector table can raise a declared LOW but never lower a declared HIGH. If you skip these fields entirely, the customer is still scored: the missing dimension is excluded and named on the score detail rather than guessed — but you are scoring on less evidence than you hold.

Note

Verifying identities with MaiGuard Verify instead of (or before) onboarding? You can pass the same declarations as risk_profile on POST /v1/verify/identity — and when that verification creates the customer record, they carry over automatically. See Verify — declared risk profile.

Endpoint matrix

MethodEndpointPurpose
PUT/v1/customers/:customerId/onboardCreate or update a customer onboarding profile.
GET/v1/customersList and search customers.
GET/v1/customers/statisticsPortfolio onboarding metrics (optional).
GET/v1/tenant-listsList configured tenant lists.
POST/v1/tenant-lists/:id/entriesAdd an entry to a tenant list.

Examples

1// PUT /v1/customers/:customerId/onboard (same ID as userId in scoring)
2const response = await fetch('https://api.maiguard.com/v1/customers/customer_001/onboard', {
3  method: 'PUT',
4  headers: {
5    'Content-Type': 'application/json',
6    'Authorization': 'Bearer pk_live_<your-key>',
7  },
8  body: `{
9  "riskProfile": "medium",
10  "partnerOnboardedAt": "2025-06-01T00:00:00.000Z",
11  "countryCode": "NG",
12  "entityType": "individual",
13  "displayName": "Amara Osei",
14  "kycStatus": "verified",
15  "kycTier": 2,
16  "pep": false,
17  "expectedVolume": 5000000,
18  "expectedVolumePeriod": "monthly",
19  "occupation": "trader",
20  "businessSector": "retail",
21  "productType": "wallet",
22  "deliveryChannel": "mobile",
23  "metadata": { "sectorRiskLevel": "LOW" }
24}`,
25});

Errors & status codes

All error responses use a consistent envelope:

error-response.json
{
  "error": "Validation Error",
  "message": "Validation failed",
  "details": [{ "path": ["riskProfile"], "message": "Required field" }]
}
200 / 201 / 202Success or async accepted
400Validation error — check details array
401 / 403Auth failure or plan/permission limit
429Rate limited — honor Retry-After header
500Transient — retry with exponential backoff

Rate limits

Per-endpoint limits for transaction scoring differ from onboarding. See the full rate-limit table when integrating scoring alongside onboarding.

Global guard

Applies to all endpoints — see API reference for limits.

429 handling

Honor Retry-After and RateLimit-* response headers.

Full rate limits & scoring docs

Was this page helpful?