Skip to content

Cross-product data flows

Where user data crosses Zev product boundaries, and on what basis. This page is the DPO's map of the ecosystem's internal data movement.

For per-product flows (inside one product), see that product's data-flows.md.

The big picture

graph LR
  User((User)) -->|signup, login, MFA| ZevID
  ZevID -->|identity, KYC outcome| ZevPay
  ZevID -->|identity| ZevCommerce
  ZevID -->|identity| ZevCloud
  ZevID -->|identity| ZevWorkspace

  ZevPay -->|KYC report| ZevID
  ZevPay -->|enrollment sync| ZevID
  ZevCommerce -->|enrollment sync| ZevID
  ZevCloud -->|enrollment sync| ZevID
  ZevWorkspace -->|enrollment sync| ZevID

  ZevCommerce -->|ZPIP: zevpay.checkout.write| ZevPay
  ZevCommerce -->|ZPIP: zevpay.kyc.status| ZevPay

Recurring flows

Identity (ZevID → every product)

When a user signs into any Zev product, ZevID returns an identity token bound to that product. The token carries the user's accountId, email, name, and (for products that opted into it) basic profile fields. No cross-product data leaks via the token — products only see what's in the user's signin-time OAuth scope.

Documented per product in each product's data-flows.md under "Signup / onboarding."

Enrollment sync (every product → ZevID)

Every product reports the user's enrollment + tier + sub-entities into ZevID via a PUT call. See enrollment-sync.md for the contract.

KYC outcome write (ZevPay → ZevID)

When ZevPay completes KYC for a user, it writes the outcome into ZevID's kyc_verifications table via POST /v1/internal/kyc-report. ZevID becomes the source of truth for "has this user been KYC'd at all in the ecosystem." Other products read this when they need a basic "is this a real human" check; they don't re-run KYC.

Lawful basis: legitimate interest (ecosystem trust requires a single KYC record; the user accepted ZevPay's KYC terms at the point of submitting BVN/NIN).

KYC tier read (other products → ZevID, then optionally ZPIP to ZevPay)

For products that need the ZevPay KYC tier (e.g. for transaction-limit checks elsewhere), the path is:

  • Read enrollments[zevpay].metadata.personalKycLevel from ZevID (synced via the enrollment-sync contract; no ZPIP required, no user consent).
  • Read deeper detail (document statuses, match scores) → call ZevPay directly via ZPIP zevpay.kyc.documents.read (service+user; consent required).

Calling ZevPay directly is reserved for genuine domain-deep integrations. For "is this user verified?" the ZevID-side read is sufficient.

ZPIP cross-product calls (any product → any product)

When a product needs to do something more on the user's behalf at another product (e.g. ZevCommerce issuing API keys at ZevPay for the user's merchant account), it uses ZPIP with service+user auth and a consent screen on accounts.zevop.com. See zpip.md.

Webhooks back to the caller

For events that callers need to react to (consent revoked, capability deprecated, tier changed), ZevID and product owners dispatch webhooks. Subscribers are documented in each product's third-parties.md — webhooks effectively make the receiving product a processor of the dispatching product's events.

Cross-border movement inside Zev

All Zev product DBs run in regions consistent with NDPA expectations. We don't cross borders between Zev products. Cross-border movement only happens via external processors — documented per product in third-parties.md.