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

what happenedthe factswhen it happenedwho acted · 0..1web_person_visitedLaurie viewed /pricingpage_url: /pricingchannel: organic2026-08-01T09:14:00Zlaurie · visitor
One event is one fact: what happened, the values that describe it, when it happened in the source, and, when a human acted, who.

Four parts, always:

  1. 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.
  2. Metadata: the typed facts, exactly the fields the event's data model declares. Promoted fields become filterable columns and scoring inputs.
  3. Timestamp: when it happened in the source system, not when it was pushed. History arrives late all the time; ordering stays honest.
  4. The acting person, when there is one: person-scope events carry personId and contactId, which is what makes "who did this" a query.

2. Events form a timeseries

eventspiedpiper.compage visitpage visitsignupmessagemeetingdeal.updatedinvoice.paidresearchactivationconversationsrevenuetime
One lane per company. The same timeline carries research, activation, conversations, and revenue, in the order they truly happened.

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)SaysCarries
web_person_visitedSomeone viewed a page on your sitepage_url, channel, utm_source, the visitor
job_changeA person you know changed jobsthe new title, company, linkedin_url
hiring_job.postedThe account is hiring (your custom data model)title, location, job_url
slack_messageSomeone wrote in a shared channelchannel_name, message_text, the author
calendar_meeting.scheduledA meeting was bookedscheduled_start, invitees, provider
stripe_invoice.paidA subscription payment landedamount_paid_cents, currency, invoice_id

Use case: track a champion

The signal no CRM sees: your champion changes jobs.

your championsame person, new companyhooli.commessagemeetingmessagejob_changesliceline.commessagemeetingtime
Richard carried the relationship at Hooli. One job_change event moves his lane to Sliceline: same person, full history, a new warm account the moment it happens.

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 and emf__ 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.