Companies and events
Why every event lands on exactly one company, what it records about its source, and why you correct one by sending another.
An event is one fact: something that happened with a company, at a moment in time, from one source. A message was sent, an invoice was paid, a meeting was booked, a deal moved. Everything else in ackDB, counts, scores, segments, answers, is a read over events.
1. This is an event
Four parts, always:
- Source and type: which data model produced it, and what kind of fact it is. Sent bare (
person_visited), stored source-prefixed (web_person_visited), which is the form every read and filter uses. - Metadata: the typed facts, exactly the fields the event's data model declares. Promoted fields become filterable columns and scoring inputs.
- Timestamp: when it happened in the source system, not when it was pushed. History arrives late all the time; ordering stays honest.
- The acting person, when there is one: person-scope events carry
personIdandcontactId, which is what makes "who did this" a query.
2. Events form a timeseries
Every company accumulates its own lane, ordered by timestamp. That is the product: not a snapshot of fields, but the sequence of what actually happened, across every source, in one place.
Events are history, so they are written once and never edited. A correction is a new event (Slack edits arrive as message_changed); removal is an explicit, guarded delete. What you read today is what you will read in an audit next year.
3. Events by example
| Event (stored) | Says | Carries |
|---|---|---|
web_person_visited | Someone viewed a page on your site | page_url, channel, utm_source, the visitor |
job_change | A person you know changed jobs | the new title, company, linkedin_url |
hiring_job.posted | The account is hiring (your custom data model) | title, location, job_url |
slack_message | Someone wrote in a shared channel | channel_name, message_text, the author |
calendar_meeting.scheduled | A meeting was booked | scheduled_start, invitees, provider |
stripe_invoice.paid | A subscription payment landed | amount_paid_cents, currency, invoice_id |
Use case: track a champion
The signal no CRM sees: your champion changes jobs.
Richard's LinkedIn key survives the move, so he stays one person. His Hooli contact edge ends, a Sliceline edge begins, and every event he ever acted in stays exactly where it happened. GET /persons/{personId}/events reads his whole story across both companies, a score criterion on event:job.change surfaces accounts a champion just landed at, and a champions segment with its changes feed turns the move into a same-day alert.
4. What happens when one lands
One committed event immediately shows up everywhere reads happen, because nothing derived is stored:
- On the company's timeline and in cross-company event queries.
- In
evc__counts andemf__latest-value columns. - In every behavior criterion of every score whose window covers it.
- In segment membership, and on the changes feed if it flips a record in or out.
The write path is one door, POST /ingest, covered end to end in ingestion.