Compliance - Adapters
Location: api/nextango/apps/compliance/services/kyc/
Last Updated: 2026-07-06
Overview
KYC vendor adapters translate between the platform's credential model and third-party identity verification services. Each vendor gets its own adapter file. Adding a vendor requires no changes to the base contract or to existing adapters. The platform ships with a Persona adapter; additional vendors slot in as sibling files.
Every adapter speaks the same two-method contract and returns the same VerificationResult dataclass, so the webhook view, onboarding service, and credential state machine do not depend on any vendor-specific logic.
KYCVendorAdapter (base)
Abstract base in compliance/services/kyc/base.py. Concrete adapters implement both methods.
initiate_verification(user, credential_key) → str
Starts a verification session for a credential. Returns a URL the storefront or POS redirects the customer to. The adapter passes credential_key to the vendor as whatever field the vendor uses to echo back identifiers on webhooks (Persona: reference-id).
handle_webhook(payload) → VerificationResult
Parses an inbound vendor webhook body into a normalised outcome. Adapters never mutate credentials directly; they return a VerificationResult and leave state transitions to the webhook view.
VerificationResult
Dataclass returned by every handle_webhook call.
| Field | Type | Purpose |
|---|---|---|
credential_key | str | The _key of the credential this event refers to. Empty string means the webhook could not be tied to a credential. |
outcome | str | Tri-state: 'approved', 'rejected', or 'unresolved'. |
raw | Any | The original payload, preserved for audit storage and debugging. |
vendor_reference_id | str | Vendor's own identifier for the event (e.g. Persona inquiry id), stored on the credential's verificationData.vendorReferenceId for reconciliation. |
Outcome semantics:
approved, terminal success; the webhook view transitions the credential toactive.rejected, terminal failure; the webhook view transitions the credential torejectedand records arejectionReason.unresolved, non-terminal or unknown status (e.g. inquiry still in progress). The webhook view acks the event with200 OKbut does not mutate the credential.
PersonaAdapter
Persona uses inquiry-based verification. When a customer starts KYC, the adapter creates an inquiry and passes the credential _key as the reference-id. Persona echoes reference-id back on every webhook, which is how the webhook view resolves the credential.
Reference: Persona: Create an Inquiry.
Settings
| Setting | Purpose |
|---|---|
PERSONA_API_KEY | Bearer token for the Persona REST API |
PERSONA_TEMPLATE_ID | Inquiry template id used for storefront KYC |
PERSONA_WEBHOOK_SECRET | HMAC secret for signature verification in the webhook view |
initiate_verification(user, credential_key)
POST https://withpersona.com/api/v1/inquiries with:
data.attributes.inquiry-template-id=PERSONA_TEMPLATE_IDdata.attributes.reference-id=credential_keydata.attributes.fields.email-address.value=user.email
On success returns attributes.hosted-session-token-url (or session-token as fallback). On non-2xx raises RuntimeError including the HTTP status and response body.
handle_webhook(payload)
Extracts data.attributes.reference-id and data.attributes.status. Outcome is derived from the status string:
| Persona status | Outcome |
|---|---|
approved, completed | approved |
failed, declined, expired | rejected |
| anything else | unresolved |
Returns a VerificationResult populated with credential_key=reference-id, the resolved outcome, the raw payload, and vendor_reference_id=data.id.