Contacts & consent
List wey you fit defend, no be just list wey you get
Anybody fit hold ten thousand e-mail addresses. Di question wey matter — di one wey regulator, provider and your own conscience dey ask with small small different words — na where each of dem come from and on wetin basis you fit write to am. ClickSomo make am become stored fact instead of something you go need to reconstruct.
Identity na di e-mail address
One contact per address per project, di database dey enforce am, no be hope. Dem dey normalise addresses when dem dey write am, never when dem dey read am — if you normalise on read, di constraint go just be decoration wey go let Anna@ and anna@ enter together.
Dem dey store phone numbers in E.164 where dem dey exist, but phone number na attribute, no be identity. No identity-resolution graph dey, no merge protocol and no audit trail of merges, because every contact wey dey here come through channel wey already get address: import, form, subscribe, or your own code.
Dem dey store di name complete
display_name dey kept exactly as dem give am and dem never split am into parts. Name splitting na bug factory — "van der Berg", "Kim Min-jun", mononyms, titles, and every ordering convention wey no be Anglo — and wrong split go show inside merge tag as "Dear van,", wey worse pass if no personalisation dey at all.
Structured parts dey exist only for sources wey supply dem already separated. Nothing dey derive dem. And name wey person type imsef dem never go overwrite am with later CSV wey just happen to observe something different, because last-write-wins go let di sloppiest import win.
- 1IdentityE-mail, normalised. Unique inside di project.
- 2NameDem store am complete.
first_namena derived at render time, dem never store am. - 3AttributesAnything wey you like, as JSON. Na yours, and you fit interpolate am as
attrs.*. - 4Tags and listsTags na first class and dem dey live inside one store; lists na explicit membership.
- 5ConsentOne row per channel, with lawful basis and im evidence.
- 6EventsAppend-only log of everything dis person do. Na di table wey di product hang on top.
- ×NevarNo password, no hash, no session, no API key. Column wey dey authenticate person go be incident, no be feature.
Scoped to tenant and project, both of dem
The consent ledger
Absence no be permission
One row per contact per channel — e-mail, SMS, letter, push — each one carry state, lawful basis, source, di evidence, di timestamps and di jurisdiction. Contact wey no get basis row for one channel no fit receive anything on dat channel. No be "dem flag am". E no fit.
consent
Person actively ask. Form submission with di evidence stored, double opt-in, checkbox wey di person tick imsef. Dis one na di strongest basis and na di only one wey form fit produce.
legitimate_interest
Di best wey CSV import fit land as. Spreadsheet no fit remember person agreeing to something, so dem never go allow am claim say dem agree — and to re-upload di same file no go upgrade am.
contract
You dey write to dem because of something wey both of you be party to. Di service reminder, di renewal notice, di thing wey dem go vex if dem no receive am.
Before campaign send, you go see di basis mix of im audience: how many of dis people ask, how many you dey reach under legitimate interest, and how many dey there because of contract. Na one number wey don stop more bad sends pass any warning banner wey dem ever write.
Why dis one stricter pass wetin e need to be for you: e no really about you. Bulk sending reputation na shared resource, and one careless list go poison di well for everybody wey dey send from di same infrastructure. Dem dey watch per-tenant complaint and bounce rates, and account wey im rates pass di threshold dem go throttle am here — before provider go do am to everybody at once.

Behind every row inside di list, somebody say yes.
Four ways to enter
Every route enter your list na you, dey act on your own data
No service-token ingest lane dey and no sibling product dey quietly write into your contacts. If you want somebody on a list, one of dis four na im put dem there.
CSV import
Dem map am column by column, dem normalise am when dem dey write am, MX-check am so say dead domain and domain wey get typo go catch before e hard-bounce, and e land with basis wey you set am yourself. Di import go get id, and every contact wey e create go point back to am.
ClickSomo form
Dem render am from di same document like every oda tin, with validation, honeypot and rate limit. Submission na di only road wey fit produce consent well-well, because na di only one wey di person dey present.
Page wey you build inside dash-b
Di dashboard designer dey publish pages; form wey dey one of dem go post here with your own credentials. Same validation, same consent handling, no second integration wey you go maintain.
Di public API
POST /v1/contacts with your own token, upsert semantics, idempotency key wey dem go honour. Your app don already know who sign up; na di shortest road from dat fact to funnel.
Segments na predicate, compiled
Segment na stored predicate tree wey dey over contact fields, attributes, tags, list membership and behaviour, dem compile am to SQL and cache am with count. Di operators na di ordinary ones — equals, not equals, in, not in, starts with, ends with, contains, is set, is not set — dem combine am with and, or and not to any depth wey you need.
Preview am before you use am. Di endpoint dey return real contacts, so "who dey inside dis one truly-truly" na question wey you go answer by looking, no be by sending and come find out.
Segments sef dey do double work: as audience for campaign, as condition for funnel branch, and as trigger on im own — segment_enter go start run di moment somebody become eligible.
- 1Tag
tags contains "regular" - 2Attribute
attrs.total_orders >= 3— na your field, na your name for am. - 3How e dey behaveNo
order.paidevent inside di last 90 days. - 4ConsentE-mail basis wey you fit use. Contacts wey no get am no dey inside di count, because dem no fit receive message anyway.
- iLook am first412 contacts, listed. Dem go count am again at send, because segment na question, no be snapshot.
Turn am to SQLCount dey cache, answer dey live
Suppression, and when dem dey check am
Suppressions dey project-scoped and global: hard bounces, complaints, unsubscribes, manual entries and role addresses. Dem dey check dem at send time, and dem go check am again inside di sending service sef.
Dat second word na im do di work. Funnel wey get three-day wait inside get three-day window wey somebody fit unsubscribe, and system wey check suppression when dem queue di message go still mail dem anyway. To check at send na di difference between to honour unsubscribe and to record am.
Erasure dey work di same way: deletion request go remove di contact and dia message history here, and e go reach every sibling service wey hold copy through di same internal lane wey di rest of di family dey use. Na one place to ask and one tin wey go happen.