Verification & compliance
A Principal is the accountable identity behind every agent. Verification is how the platform — and the obligated entity behind it — can prove a transaction traces back to a real, vetted subject. This page covers the two verification modes, the attestation record, and the one rule that governs everything: verification gates transacting, not registering.
Why verification exists
Autonomous agents move money. Behind every agent there must be a real, accountable subject — a person or a merchant — who has been through customer due diligence (CDD). That subject is the Principal (prn_), and verification is the step that establishes who it is and that someone is on the hook for it.
The platform never lets a prn_ reach an active mandate without a verification decision behind it. The decision answers two compliance questions at once:
- Identity — is this a genuine, screened individual (KYC) or business (KYB)?
- Accountability — which obligated entity bears the CDD liability: the Client, or Mandate Labs?
That second question is the one most platforms get wrong. Mandate Labs makes it explicit through the verification mode, set per Program or per Principal. The mode decides both who runs the diligence and who is liable for it.
Verification attaches to the Principal, not the agent. You verify a prn_ once; every agt_ it owns inherits that verified standing. You never re-verify per agent.
The two modes
Every Principal is verified one of two ways. The mode is declared in the verification.mode field on POST /principals and recorded on the Principal.
client_attested verified now
The Client is the obligated entity and attests, on the record, that it performed CDD on the end-user in its own program. Mandate Labs does not re-run KYC — it stores the Client's attestation as liability-bearing evidence and verifies the Principal immediately.
The call returns 201 and the Principal lands at VERIFIED in a single round trip. A VerificationAttestation row is written recording who attested, what was attested, the full statement, and an optional pointer to the Client's own KYC case.
platform_provider pending KYC
Mandate Labs runs KYC/KYB via an integrated verification provider (MetaMap today; Sumsub/Persona on the roadmap). The end-user completes a hosted verification flow, and Mandate Labs bears the diligence responsibility.
The call returns 202 with a verification_session you hand to the user, and the Principal starts at PENDING. It moves PENDING → IN_REVIEW → VERIFIED or REJECTED as the provider responds. You learn the outcome by polling the Principal or by subscribing to the verification webhook.
| Dimension | client_attested | platform_provider |
|---|---|---|
| Who runs CDD | The Client, in its own program | Mandate Labs, via a provider |
| Who bears liability | The Client (obligated entity) | Mandate Labs |
| HTTP response | 201 Created | 202 Accepted |
| Initial status | VERIFIED | PENDING |
| Evidence stored | VerificationAttestation row | Provider session + result |
attestation block | Required | Omitted |
| How you learn the result | Immediate (in the response) | Poll Principal or webhook |
The attestation object
For client_attested mode, the attestation block is the compliance artifact. It is persisted as a VerificationAttestation — the liability-bearing record of the Client's CDD claim, retained per the Data Retention Schedule. It captures four fields:
| Field | Type | Required | What it records |
|---|---|---|---|
attested_by | string | Yes | Who at the Client made the attestation — typically a compliance contact (e.g. an email). |
scope | string | Yes | What was attested — the level/type of CDD performed (e.g. "Tier-2 CDD"). |
statement | string | Yes | The full attestation text — the Client's explicit claim that it verified this subject. |
evidence_ref | string | No | A reference to the Client's own KYC case/record, so the underlying evidence can be located on audit. |
The stored row additionally binds the attestation to the subject (principal_id), optionally to the program_id it was made under, and stamps an attested_at timestamp at the moment of registration. This is what lets a regulator, an issuer, or an internal auditor trace any agent transaction back to a named human or named approver who vouched for the underlying subject.
Submitting a client_attested Principal is an assertion that real CDD was performed. The statement, the attester, and the evidence reference are retained as evidence. Do not attest to diligence you did not perform.
KYC vs KYB
The verification.type field declares whether the subject is a person or a business. It is set independently of the mode and is recorded as the Principal's verification type.
| Type | Subject | principal_kind | Meaning |
|---|---|---|---|
KYC | A natural person | personal | Know Your Customer — identity verification of an individual. |
KYB | A merchant / business | merchant | Know Your Business — verification of a legal entity and its beneficial owners. |
The two fields pair naturally: a personal Principal is verified KYC; a merchant Principal is verified KYB. The platform derives the verification type from the principal kind when registering, so a merchant attestation is recorded as a KYB attestation and an individual as KYC.
Verification gates transacting, not registering
This is the load-bearing rule of the whole model. An agent can be registered before its Principal is VERIFIED, but it cannot hold an active mandate or authorize a transaction until the Principal is VERIFIED.
The separation is deliberate. It lets you build out an integration — create the Principal, register one or more agents, wire up your systems — in parallel with verification finishing, without ever letting an unverified subject move money. Registration is structural; transacting is gated.
Concretely:
- Before VERIFIED — you may
POST /principals/{prn}/agentsto add agents. They exist and carry a DID, but a mandate on them is inert and anyPOST /authorizeis refused. - At VERIFIED — mandates become active and the agent can authorize within its bounds. For
client_attestedthis is true the instant the Principal is created; forplatform_providerit is true when the provider returns a pass. - If REJECTED or EXPIRED — the Principal cannot transact; it must be re-verified before any of its agents can resume.
The platform treats VERIFIED as the single gate that permits agent authority. No other status — not PENDING, not IN_REVIEW, not MANUAL_REVIEW — allows a mandate to go active.
Verification status values
The Principal's verification_status follows a defined lifecycle: UNVERIFIED → PENDING → VERIFIED | REJECTED | EXPIRED, with provider or compliance escalations routing through review states.
| Status | Meaning | Can transact? |
|---|---|---|
| UNVERIFIED | No verification submitted yet. | No |
| PENDING | Docs submitted; awaiting the provider's response (platform_provider). | No |
| IN_REVIEW | The provider flagged the case for manual review. | No |
| VERIFIED | Passed KYC/KYB — agents may hold active mandates and authorize. | Yes |
| REJECTED | Failed verification — the subject must resubmit. | No |
| EXPIRED | A prior verification lapsed — the subject must re-verify. | No |
| MANUAL_REVIEW | Escalated to the Mandate Labs compliance team. | No |
A successful verification also assigns a risk tier (LOW, MEDIUM, HIGH, or PROHIBITED) that travels with the Principal and feeds downstream risk decisions.
Worked examples
Both flows are a single POST /principals authenticated with your Client API key (X-API-Key). The mode determines the response.
Mode A — client_attested (201, immediate)
The Client onboards a merchant it has already KYB'd, attaching the attestation that records its diligence. The Principal is VERIFIED in the response.
curl -X POST https://api.mandatelabs.ai/api/v1/principals \ -H "X-API-Key: mdt_live_…" -H "Content-Type: application/json" \ -H "Idempotency-Key: prn-acme-001" \ -d '{ "principal_kind": "merchant", "program_ids": ["prg_…"], "client_customer_ref": "acme-001", "profile": { "display_name": "Acme Corp", "email": "[email protected]", "country": "US" }, "verification": { "type": "KYB", "mode": "client_attested", "attestation": { "attested_by": "[email protected]", "scope": "Tier-2 CDD", "statement": "Acme performed full Tier-2 customer due diligence on this merchant, including beneficial-ownership and sanctions screening.", "evidence_ref": "kyc-case-123" } } }'
{
"id": "prn_1718900000000_a1b2c3d4",
"client_id": "cli_…",
"principal_kind": "merchant",
"client_customer_ref": "acme-001",
"verification_status": "VERIFIED",
"verification_mode": "client_attested",
"risk_tier": "LOW",
"programs": ["prg_…"],
"agents": [],
"verification_session": null,
"created_at": "2026-06-27T12:00:00Z"
}
Status 201, verification_status: "VERIFIED", and verification_session: null — the agent you add next can be granted an active mandate right away.
Mode B — platform_provider (202, pending)
The Client onboards an individual and asks Mandate Labs to run KYC. No attestation is sent; the response carries a verification_session to hand to the user.
curl -X POST https://api.mandatelabs.ai/api/v1/principals \ -H "X-API-Key: mdt_live_…" -H "Content-Type: application/json" \ -H "Idempotency-Key: prn-jdoe-001" \ -d '{ "principal_kind": "personal", "program_ids": ["prg_…"], "client_customer_ref": "jdoe-001", "profile": { "display_name": "Jane Doe", "email": "[email protected]", "country": "US" }, "verification": { "type": "KYC", "mode": "platform_provider" } }'
{
"id": "prn_1718900111000_e5f6a7b8",
"client_id": "cli_…",
"principal_kind": "personal",
"client_customer_ref": "jdoe-001",
"verification_status": "PENDING",
"verification_mode": "platform_provider",
"risk_tier": null,
"programs": ["prg_…"],
"agents": [],
"verification_session": {
"provider": "pending",
"session_id": "vs_9f8e7d6c5b4a3210",
"hosted_url": "https://verify.mandatelabs.ai/s/vs_9f8e7d6c5b4a3210",
"expires_at": "2026-06-28T12:00:00Z"
},
"created_at": "2026-06-27T12:00:00Z"
}
Status 202, verification_status: "PENDING". Direct the user to verification_session.hosted_url; when they finish, the status advances to VERIFIED (or REJECTED). Poll GET /principals/{prn} or subscribe to the verification webhook to learn the outcome rather than blocking on the response.
In the sandbox, verification is auto-completed so you can drive the full chain without a real provider round trip — the playground's Mandate Labs Client attests on your behalf. The two-mode distinction above is the production behavior.
Compliance note: passporting your own CDD
client_attested is the mechanism by which a Client that is itself an obligated entity — a regulated fintech, an issuer, a licensed program — passports its existing CDD into Mandate Labs without duplicating verification. You already screen your customers under your own program; client-attested lets that screening stand, while the VerificationAttestation preserves the audit trail proving you did.
The liability split is the point. Under client_attested the Client remains the obligated entity and bears KYC liability; the attestation is the evidence of that responsibility. Under platform_provider, Mandate Labs runs the diligence and carries it. Choose the mode that matches who is actually accountable for the end-user — and never attest to diligence you have not performed.