Resources

Identity verification

How Pierflow verifies member identity at enrollment time, what we store, and how the disposition rules drive policy issuance.

Identity verification runs inline inside POST /v1/enrollments. You don't call it directly — you submit the member's NIN or BVN as part of the enrollment body and Pierflow returns the verification disposition alongside the enrollment record. This page documents how that step works, what we store, and how the disposition rules drive what happens next.

What we store#

We hold the absolute minimum needed to verify and reconcile, and we treat the rest as sensitive PII subject to NDPC.

FieldHow it's stored
nin / bvn (plaintext)Never persisted. Used in-memory during the verification call, then discarded.
nin_hash / bvn_hashSHA-256(IDENTITY_HASH_SECRET || identifier). Stable across calls for dedup; not trivially reversible.
nin_last4 / bvn_last4Last 4 digits for display in the portal.
encrypted_payloadAES-256-GCM ciphertext of the original identifiers + supplied profile. Keyed by IDENTITY_ENCRYPTION_KEY (env). Decrypted only by the identity service when re-verifying.
full_name, date_of_birth, sexPlain columns — needed to display the member in the portal and to compare against future re-verifications.
The encryption key + hash secret are env-only. Rotate by issuing a new key, re-encrypting the column, then revoking the old key. The identity service is the only caller permitted to decrypt; route handlers never see the plaintext after the initial submission.

Verification providers#

The implementation is provider-pluggable. The active provider is selected by the IDENTITY_PROVIDER env var.

ProviderWhen to useBehaviour
stub (default)Sandbox, dev, early production while NIMC credentials are being procured.Returns AUTO_APPROVED at confidence 95 for any name. Magic names TEST_FAIL and TEST_SOFT force REJECTED / SOFT_REVIEW respectively — useful for exercising downstream branches.
partner_declaredWhen the fintech has already KYC'd the user upstream and you want a trust-but-audit record.Returns AUTO_APPROVED at confidence 90. We record the verification row so a future dispute can be audited, but we don't re-call NIMC.
nimcProduction. Calls the real NIMC API.Not yet wired in this version. Production deployments must implement runNimc() in lib/insurance/identity.ts before going live; the stub throws if selected without an implementation.

Disposition rules#

Every verification returns a confidence score 0–100. Three dispositions follow from it:

ConfidenceDispositionEnrollment outcome
> 85AUTO_APPROVEDEnrollment proceeds normally. Policy issues on payment.
60 – 85SOFT_REVIEWEnrollment proceeds. The IdentityVerification row is flagged for ops review. Useful for ops investigation but doesn't block the user.
< 60REJECTEDEnrollment is refused. No enrollment row created. The hmo_enrollment.identity_rejected webhook fires so you can prompt the user to fix their data.

In the enrollment response#

The identity outcome is surfaced on every enrollment response:

json
json
{
  "enrollment": { /* ... */ },
  "identity": {
    "status": "AUTO_APPROVED",   // AUTO_APPROVED | SOFT_REVIEW | REJECTED
    "confidence": 95,             // 0..100
    "provider": "STUB"            // STUB | PARTNER_DECLARED | NIMC
  },
  "idempotent_replay": false
}

SOFT_REVIEW enrollments succeed but you should surface a note in your admin tooling — Pierflow staff will reach out if the verification ends up needing manual correction.

Dedup & cross-tenant rules#

The SHA-256 hash is stable for a given identifier across all your verifications, which means you can answer "have I verified this person before?" without ever storing the plaintext. The same hash across two different fintech partners is the same person, but Pierflow does not cross-link partners — each fintech sees only its own verification history.

Dedup lookup

Not currently exposed as a public endpoint. Use your audit logs or contact Pierflow support to confirm whether a NIN already has a verification on file for your partner.

Encryption at rest#

AES-256-GCM with a 32-byte key. Wire format: [12B nonce][16B authTag][ciphertext]. The key lives in IDENTITY_ENCRYPTION_KEY as 64 hex chars (32 raw bytes). Generate with openssl rand -hex 32; never commit to source control.

For sandbox + dev, an env fallback derives a deterministic key from a fixed string so the encrypted column can be read back. This fallback MUST NOT ship to production. The service throws on an absent key if NODE_ENV=production.

Auditability#

Every verification — including rejections — writes an IdentityVerification row with the disposition, provider, confidence, field-level checks, and the raw provider response. Combined with the immutable HmoEnrollmentEvent log, this gives Pierflow and the fintech a complete trail for any dispute.