Data inventory — ZevSend
Categories of data ZevSend holds. Field-by-field detail lives in the schema folder under zevsend-backend/src/common/database/schema/. This page is the DPO's overview — the schema is the source of truth for exact columns and types.
What we don't store
ZevSend does not store the customer's email, password, MFA secrets, or KYC data. Those live in ZevID. We address customers by their accountId (a UUID issued by ZevID) and read identity fields on demand via the ZevID internal API.
Identity references (not the identity itself)
| Category |
What we hold |
Schema |
| Account pointer |
accountId (UUID issued by ZevID) on every team-membership / billing / audit row |
every owning table |
| ZevID enrollment sync |
Whether this account is enrolled in ZevSend + their tier — written into ZevID, not stored locally |
n/a (ZevID-owned) |
Team + collaboration data
| Category |
What we hold |
Schema |
| Team |
Team name, owner account id, billing currency, sandboxState (sandbox / live), suspension state, created / updated timestamps |
teams.ts |
| Team membership |
Per-account team membership + role |
team-members.ts |
| Team invitations |
Pending invites with expiry, invited-by account id, revocation state |
team-invites.ts |
| API keys |
SHA-256 hashes of plaintext, env (test / live), type (secret / publishable), optional scoped domain_id, prefix + last-four for display, revocation state |
api-keys.ts (hash only — plaintext shown once at creation) |
Domain + sender-identity data
| Category |
What we hold |
Schema |
| Domain |
Customer-owned domain name (name, nameNormalised), verification status, admin-review status, source (manual / zevcloud), carrier id (SES identity), carrier metadata, health-check counters (consecutiveFailures, firstFailedAt) |
domains.ts |
| Brand identity |
Brand name, legal entity, support email, support URL, submission + review timestamps; locked at admin approval |
columns on domains |
| DNS records the customer should publish |
JSONB mirror of carrier-emitted records (DKIM, custom MAIL FROM, SPF, DMARC) |
column on domains |
| Display-name applications |
Customer-registered alternate brand strings per domain — submitted display string, normalised form, justification, status, review note, public id (dn_…) |
domain-display-names.ts |
| Sender-ID applications (SMS) |
Customer-submitted SMS sender id requests — value, justification, country codes, status, review note, result sender-id pointer on approval |
sms-sender-id-applications.ts |
| Sender IDs (SMS) |
Approved + carrier-registered SMS sender values |
sms-sender-ids.ts |
| SMS carriers |
Admin-managed carrier registry + routing config |
sms-carriers.ts |
| WhatsApp carriers |
Admin-managed WhatsApp provider registry (Meta Cloud today) |
whatsapp-carriers.ts |
Template data
| Category |
What we hold |
Schema |
| Message templates |
Per-channel template (email / SMS / WhatsApp) — slug, name, description, subject (email), body, plaintext, declared customer.* + brand.* variables, locale, status, price |
message-templates.ts |
| Template price overrides |
Per-country price overrides on a template |
template-price-overrides.ts |
Send / dispatch data (customer-controller, ZevSend-processor for body + recipient)
| Category |
What we hold |
Schema |
| Email messages |
Per-send row — from, to, subject, html, plain-text, template id, api-key id, domain id, status, carrier message id, error message, sandbox flag, sent / created timestamps |
email-messages.ts |
| SMS messages |
Per-send row — from, to (phone, E.164), body, template id, api-key id, status, carrier id, carrier message id, error message, sandbox flag, sent / created timestamps |
sms-messages.ts |
| WhatsApp messages |
Per-send row — from (display + WABA template id), to (phone, E.164), template variables (positional), api-key id, status, carrier message id, error message, sandbox flag, sent / created timestamps |
whatsapp-messages.ts |
| Verifications |
OTP record — channel, recipient address / phone, hashed code only (plaintext never persisted), expiry, status, attempt count |
verifications.ts |
| Suppressions |
Per-team list of recipient addresses to refuse sends to — recipient, reason (hard_bounce / complaint / manual), source (carrier / manual), detail, carrier event id |
suppressions.ts |
| Verified recipients (sandbox only) |
Per-team allowlist of recipients sandbox teams may dispatch to before going live — recipient, verification token, verified-at timestamp |
verified-recipients.ts |
Webhook + integration data
| Category |
What we hold |
Schema |
| Webhook subscriptions |
Per-team webhook target — URL, subscribed events, HMAC signing secret, active flag, failure-streak counter |
webhooks.ts |
| Webhook deliveries |
BullMQ-backed delivery records (in Redis for active jobs; failure rows retained in queue for ops triage with a cap) |
n/a — Redis-resident |
Billing data
| Category |
What we hold |
Schema |
| Plan |
Sellable plans + their per-channel feature flags, price matrix, quotas (monthlyEmailQuota, dailyEmailQuota, domainsAllowed, teamMembersMax), monthlyCreditAllowanceMinor |
plans.ts |
| Subscription |
Recurring billing schedule, plan id, next-renewal date, scheduled-plan-change + effective-at |
subscriptions.ts + team-plan-schedule.ts |
| Invoice |
Header + line items + amount + status; total billed / paid |
invoices.ts + subscription-invoices.ts |
| Payment method |
Tokenised reference (ZevPay's token; no card numbers, BVN, or bank-account credentials stored by ZevSend) |
reference only — owned by ZevPay |
| Credits ledger ref |
Pointer into ZevID's Zev Credit balance (ZevID is the canonical ledger) |
n/a — read via ZevID API |
Audit + admin
| Category |
What we hold |
Schema |
| Admin audit log |
Every admin action against a customer resource (domain approve / reject, display-name approve / reject, sender-id approve / reject, plan change, suspend / reinstate, mark-paid) — actor admin id, target resource, before / after where relevant, metadata |
admin-actions.ts |
| ZPIP token + delivery audit |
Inbound consent + outbound webhook deliveries (token id, scopes, requester product, decision) |
shared ZPIP middleware |
| Category |
What we hold |
Schema |
| Platform settings |
Admin-tunable runtime config — open-flag toggles (e.g. sms.custom_sender_id_applications_open), per-currency verify pricing maps (verify.sms.price_by_currency, etc.) |
platform-settings.ts |
Special categories (NDPA Sensitive Personal Data)
Not applicable. ZevSend does not collect biometric, health, racial / ethnic, political, religious, sexual-orientation, or genetic data. KYC outcome (the one place biometric-derived data could land) is held by ZevID — see ZevID's data inventory.