Documentation overview

Connecting your data

Everything The AI CMO knows about your customers flows through the Data page – sources on the left of the journey, your event vocabulary in the middle, and the warehouse it all lands in. This manual covers each door in, and how to confirm your data arrived.

The Data page

Data in the sidebar opens these tabs – the first two are the two directions your data can move:

  • Push – everything you send us: CSV imports, the Website SDK at a glance, the address your own systems post to, the keys they use, and recurring file feeds.
  • Pull – everything we fetch for you on a schedule: Shopify, Stripe, ad platforms, and tables in your own database, each with a freshness status.
  • Events – the vocabulary of custom events your site and systems can send (see below).
  • Catalog – the products, bonuses and other items your events refer to.
  • Activity – the record of everything that has moved, in either direction (see below).
  • Warehouse – totals, every event type as it streams in, and the meter showing how much of your plan's included volume you've used.

If you want to explore the platform before wiring anything real, Load demo data on the Push tab fills the account with a sample business you can safely play with and remove later.

The fastest start: one CSV

The Add your data (CSV) card accepts one file and works out what it is:

  • Purchase history – a customer, a date, an amount per row. The moment it imports, the Overview tab has your revenue numbers.
  • Ad performance – a date, a platform, a spend per row. Platform names are standardized automatically (GADS, PPC, and Google Ads all become Google Ads), and ROAS and CAC go live on Overview.
  • A contact list – any file with an email column becomes an audience segment in Customer Intelligence → Audiences. A consent column in it (see Marketing consent of the people your data brings in below) sets each new person's e-mail marketing consent; the confirmation step says whether one was found.
  • A combined file – one file with a record_type column can carry purchases and ad rows together. There's a sample CSV to download on the card.

You map the columns in a quick confirmation step, extra columns are always kept as customer attributes, and files up to 100,000 rows are accepted. Re-importing the same file is safe – the warehouse never double-counts an order it has already seen. Every import appears in the Uploads list, where it can also be deleted.

Connecting your apps

Integrations in the sidebar connects The AI CMO to the tools you already use. The page is organized by job:

  • Publish – Journeys and chat post here for you: Facebook, Instagram, Reddit, LinkedIn, X, TikTok, YouTube, Snapchat, WordPress. Facebook, Instagram, Reddit, TikTok, YouTube and Snapchat connect through our publishing partner's approved apps: you authorize, pick the account, and publishing works right away – nothing to review or approve on your side. A Facebook connection is one Page: the flow asks which Page, and a second Page is a second connection. Instagram connects the Business or Creator account directly, no Page needed; the composer's Instagram panel adds a Story instead of a feed post, a Reel cover, up to three collaborators, a first comment posted right after publishing, the AI-generated label and comments on or off. LinkedIn connects the profile or the organization you choose. TikTok publishes from the composer, where you choose who can see the video, allow comments, duet and stitch, and confirm the preview – TikTok requires the creator to make those choices, so chat will send you there. Reddit takes a subreddit and a title in the composer, and the moment you name the community the panel reads its rules, whether it requires a flair (and which), and which kinds of post it takes, so a post is refused on our side with the fix named rather than removed by a moderator later. YouTube takes one video per post with a title, who can see it and tags – the text is the description, and a vertical video of three minutes or less becomes a Short; Google asks which account or Brand Account to connect. Snapchat is coming: it connects a Public Profile and posts a Story (24 hours), a Saved Story (the text is its title) or a Spotlight (the text is its description) – one vertical photo or video each – and opens the day our publishing partner releases it.
  • Measure – spend and results land in your analytics automatically: Google Analytics, Google Ads, Amazon Ads. Meta Ads, TikTok Ads and LinkedIn Ads connect through the same partner – Meta as a Business Login on your business portfolio – and their spend by campaign syncs nightly into your warehouse, the same funnel your ads CSV feeds. Post performance for everything published this way syncs daily into What's Working. Google Search Console (read only) shows what your site earns in Google – clicks, impressions and average position for each search and page – in Keyword Research, the SEO Meta Generator, Link Building, chat and your SEO Strategist's work. Google grants every property on the account you sign in with; the one that matches the brand's website is used, and the card names it.
  • Reach & alert – talk to your customers and your team: email sending, HubSpot, Slack.
  • App stores – optional keys for App Store Optimization and the Helpdesk: App Store Connect, Google Play and Apple Ads. App Store Optimization reads the public stores without them; see The app store keys below.
  • Commerce – your sales land in your warehouse on their own: Shopify orders and products, Stripe payments. Each card has a Sync now button, so your numbers start the moment you connect; the nightly sync keeps them current and finishes any large backfill. Each new buyer or payer also becomes a contact of the brand – never someone erased on request – with the store's own e-mail marketing consent (see Marketing consent of the people your data brings in below). A Stripe restricted key belongs to the one Stripe account it was created in, so for each brand create the key inside that brand's Stripe account and connect it under that brand; the card names the account it reads.

Connecting is one click: Connect opens a secure sign-in window for that platform, you approve, and you're done. A connection belongs to one brand: if your account runs several, you are asked which brand the account is for before the sign-in window opens, with your current brand preselected. Integrations shows the connections of the brand you are working in; switch brands in the header to see another's. Connected to the wrong brand? Move to… on the connected card re-files it, and its data lands under the new brand from then on. Once an ad platform is connected, its spend syncs into your analytics on its own – no import needed.

You can also connect from chat. When something you ask for needs a platform that isn't connected yet, chat holds that step, says so in a small card and offers Connect, which runs this same sign-in for the brand the chat is working for. The step waits, uncharged, until you've connected and said yes. (An audit of an ad account only finds out when it reaches the account; its credits are then returned.)

The app store keys

The three cards under App stores take a key instead of a sign-in window. Connect asks which brand the key is for when you run several, then opens a dialog with the steps. The key is checked live with the store before it is saved, and it is stored encrypted. Manage takes a new key; when a store stops accepting one, the card says so, with Replace the key. Nothing changes in either store unless someone on your team sends it – a listing from App Store Optimization, a reply from the Helpdesk.

App Store Connect takes a Team key with Admin access, made in App Store Connect under Users and Access › Integrations › App Store Connect API › Team Keys (an Individual key does not work; only an Admin key can request the App Store analytics reports). Enter its Issuer ID and Key ID and choose the .p8 file Apple offers once. When the key works, pick which of the account's apps belong to the brand. It adds your real listing – the private keyword field too – your App Store reviews in the Helpdesk, and App Store analytics. Nothing is submitted for review.

Google Play takes a Google Cloud service account's JSON key. Turn on the Google Play Android Developer API in its project, then in Play Console invite the service account's e-mail under Users and permissions with View app information and download bulk reports, Reply to reviews and Manage store presence for the app – Google shows a listing to a key only through a draft edit, which is deleted straight after reading. Name the app's package, like com.example.app (several apps: separate them with commas). Also read the monthly store reports adds the reports bucket (gs://pubsite_prod_…, from Download reports › Statistics › Copy Cloud Storage URI): the store listing visitors, installs and the searches that brought visitors. Google opens the bucket only to a user whose View app information permission covers the whole account. Play Console can take a few hours to apply a new user's permissions.

Apple Ads signs in with a key pair made for the brand: Create the key, then have an Apple Ads account admin invite an API user (API Account Read Only is the narrowest role). Signed in as that user, paste the public key under Account Settings › API, and enter the client ID, team ID and key ID Apple Ads shows there. It adds Apple's own search popularity, 0–100, for your searches. Read only – no campaign is created or changed.

Platform feeds

There are two ways data arrives, and they live in different places because only one of them holds passwords to your other accounts.

  • You send it to us – your own systems, or a file. No connector, no setup: see Sending data from your own systems below.
  • We fetch it for you – Shopify, Stripe, Google Ads, Meta, or a table in your own database. These need access to those accounts, so they are set up by your workspace admins in the admin portal. The Pull tab shows each one and how fresh it is; everyone can see the status.

We pull straight from your database

If the data you want in your warehouse already sits in a PostgreSQL or MySQL database – an orders table, a player roster, a games catalogue – you can skip exports entirely. Give us read-only access to that one table and we collect new rows on a schedule you choose.

Your admins set it up in the admin portal under Data Sources → Add Data Source → We pull from your database:

  1. Create a read-only database user. We only ever read – a user that can do nothing else is the right shape for this.
  2. Enter the connection – host, port, database, username, password. The password is stored encrypted, is never shown again, and never appears in any response. Your database must be reachable from the internet; if it sits behind a firewall, your team allowlists our address.
  3. Say what to pull – the table, and a column that only ever grows (an id, or an updated_at timestamp). Each collection continues from where the last one stopped instead of re-reading everything. Leave the column blank and we re-read the whole table each run.
  4. Say what the rows are – events (orders, transactions), your customer roster, or a catalogue. Same choice as a CSV import, and the same rules apply afterwards.
  5. Press Test connection. It proves the credentials work and the table is readable before anything is saved, and shows you the table's columns.

From then on it runs on the schedule from the Settings tab – hourly, daily or weekly – and Sync now collects on the spot. A row we have seen before replaces itself rather than double-counting, so neither re-runs nor overlapping collections can inflate your numbers. Every run is recorded on Data → Activity, and the source's freshness shows on the Pull tab like every other feed.

Snowflake is on the source list and our team makes the connection – add it and the config page tells you what we need. Other warehouses are arranged with us; ask and we will tell you what is involved.

We pull from your API

Many systems have no export and no webhooks, but do have an API – a CRM, a booking engine, a loyalty platform, an SMS gateway's list of people who replied STOP. Give us the endpoint and we fetch it on a schedule, the same way we pull from a database.

Your admins set it up in the admin portal under Data Sources → Add Data Source → We pull from your API:

  1. The endpoint. The URL, GET or POST, and how we sign in – a bearer token, a key in a header or in the query string, or basic auth. The key is stored encrypted, never shown again and never appears in any response. Extra headers go in a small list of their own.
  2. How we read the answer. Where the records sit in the JSON (for example data.items, or nothing when the response is the list itself), and how to ask for the next page – a page number, an offset, a cursor the response hands back, or a next-page link. If the API can return only what changed after a time, name that parameter and each run asks just for what is new.
  3. What the records are. Events, your customer roster, a catalog – the same choices as a file – or an opt-out list: say which field holds the person (a phone number, an e-mail or your own customer id) and what the list means: marketing opt-out, block everything, or a watch list. Opt-outs land straight in that brand's suppression lists and every send checks them from then on.
  4. After import, optional. We call your API back for every record we now hold – typically to delete it at your end. That is how an SMS gateway's STOP list stops being permanent: the opt-out lives with us, where a person can be lifted again, instead of in a list nothing on your side ever clears. A record is only called back once it is safely on our side.
  5. Press Fetch a sample. It runs one request with what you typed, shows the first records and their fields, and fills the field pickers from your real data – before anything is saved.

From then on it runs on the schedule from the Settings tab, and Sync now fetches on the spot. Records we have seen before replace themselves, an opt-out already on the list is skipped, every run is on Data → Activity with the after-import count in its receipt, and the source's freshness shows on the Pull tab. Our requests only ever go to public addresses, so an API on a private network needs to be exposed to us first.

Sending data from your own systems

If your data lives in systems of your own – a player database, an order system, a nightly export – Data → Push is where you wire them in. Nothing about them needs to be modern: anything that can make an HTTPS request, or produce a file, can feed The AI CMO.

Where it lands. One endpoint, whichever warehouse you are on. If you move to a dedicated cluster later, the same address follows you – there is nothing to re-point and no second URL to learn. The Push tab tells you which warehouse it is landing in right now.

The address. One endpoint takes everything. Send a single event or up to 1,000 in one request. Sending the same event_id twice replaces the first rather than adding a second, so retries, restarts and re-sending yesterday's file never inflate your revenue.

The keys. Issue one key per system, named after that system. Two things are worth knowing:

  • A key is shown once, when you create it. We store only a fingerprint, so nobody – including us – can read it back to you. If it goes missing, revoke it and issue another.
  • A key can only send data in. It cannot read your customers, change anything, or delete a single row. Even if one leaked, that is the whole of what it could do.

You can pin a key to the fixed IP addresses your systems send from, and give it an expiry date. Revoking one takes effect immediately and affects nothing else.

Daily files. The same key works for file uploads, so a scheduled job can post your nightly export directly instead of someone opening a browser to drag it in.

Sending plans

The same address and key take your budget, forecast and target too, so a finance system can keep the Flash Report's plans current without anyone retyping them. Name the brand the plans belong to with ?client_id=<brand id>, and send a plans list – up to 2,000 rows in one request, on their own or beside events:

json
{
  "plans": [
    { "plan_type": "budget",   "metric_key": "revenue",  "year": 2027, "month": 1,   "value": 120000 },
    { "plan_type": "forecast", "metric_key": "ad_spend", "year": 2027, "quarter": 1, "value": 36000 },
    { "plan_type": "target",   "metric_key": "revenue",  "year": 2027, "month": 1,   "value": 40000, "channel": "paid_search" },
    { "plan_type": "forecast", "metric_key": "revenue",  "year": 2027, "month": 2,   "value": null }
  ]
}
  • plan_type is budget, forecast or target.
  • The period is a month (1–12), a quarter (1–4) or "period_type": "annual". A quarter or a year is spread evenly across its months for totals such as revenue and spend; a rate such as ROAS applies to each month as it is.
  • Sending the same plan, metric, period and channel again replaces the value. "value": null removes it.
  • metric_key is one of your plan lines – the key shown when you click a line's name in Plans (ggr, ngr, bonusy_10_ggr …) – or one of the report's own measures: revenue, ad_spend, roas, purchases, aov, new_customers, total_customers, returning_customers, reactivated_customers, lapsed_customers, customer_continuity, revenue_per_customer, impressions, clicks, ctr, cpc and click_to_purchase; cost and conversions are understood too.
  • A value can be negative – a line that plans a loss.
  • An approved budget cannot change, so budget rows for a month an approved budget covers are refused – send new expectations as a forecast.

A value someone types in Plans on the Flash Report takes the place of a loaded one, and the plans editor shows the loaded values in grey.

Sending actuals

Some plan lines are your own accounting – GGR, NGR, bonuses, licence fees – and only your systems know the figure. Send it with the same address and key, as an actuals list (up to 5,000 rows, on their own or beside events and plans), and the Flash Report compares each line with its plan:

json
{
  "actuals": [
    { "metric_key": "ggr", "date": "2026-11-03", "value": 104250.50 },
    { "metric_key": "ngr", "year": 2026, "month": 11, "value": 2114040 },
    { "metric_key": "ggr", "date": "2026-11-03", "value": null }
  ]
}
  • metric_key is the line's key, as shown in Plans.
  • Send a date for one day, or a year and month for the whole month. Daily figures add up across the period and are compared with the plan up to the last day you sent; a whole month's figure is compared once the month has ended. Rates and levels (ARPU, active players) are sent per month.
  • Sending the same line and day or month again replaces the figure; "value": null removes it. A figure can be negative.
  • The report never works these lines out from other data. Until a figure arrives, the line reads not reported yet.

How we know it is the same person

It does not matter which door the data came through – a CSV, a file feed, the Website SDK, a webhook from your own systems, a pull from your database, a store sync. Every one of them registers the identifiers it carries against the same person.

  • A person is keyed on the identifier you give us – your own customer ID or their e-mail, whichever your data carries; when both are known they are kept together as one person. The anonymous visitor ID the website gave them comes last, until they identify.
  • Every other identifier we see for them is kept alongside it, so someone who arrives first as a cookie and later as an e-mail ends up as one record rather than two.
  • Matching is exact. Nothing is guessed, and two people sharing a laptop stay two people.
  • What you upload wins. A name, a status, a lifetime value or a "customer since" date from your roster survives the website traffic that arrives afterwards.
  • E-mail and your own ID are treated as hard identifiers – each belongs to one customer within a brand. What to do when two records turn out to be one person is under Customer Intelligence.

If you can put an e-mail and your own ID in the same file, do. It is the single thing that makes every other door line up behind it.

A person your data adds to your contacts starts as "Not granted" for marketing e-mail unless the data itself says they agreed – nothing is assumed. "Not granted" stops marketing e-mail only; service e-mail (a journey set to Service) still reaches them, and the person's record shows it under Compliance. It changes when consent is given – the Marketing e-mail box on their record, or your landing page's consent box.

  • Files (the Add your data card, customer rosters, event files). A consent column is read for each new person: a yes (true, 1, yes, ano, áno, subscribed, opted_in, confirmed_opt_in …) is recorded as their consent, with the file it came from; a no or a blank cell leaves them "Not granted". A column counts when its name says both that it is consent and which channel it is for, for example email_consent, email_opt_in, email_marketing_consent, marketing_consent, accepts_marketing, newsletter_opt_in, Email Subscribed, Email Subscription Status, and Shopify's own customer export columns Accepts Email Marketing and Accepts SMS Marketing. A bare subscribed, opt_in or newsletter column is not read as consent (it often means a paid plan or a product setting), nor is a column holding a date, an IP address or an opt-in level. On an event file, the person's newest row that answers decides. A file without such a column adds everyone new as "Not granted". Phone numbers in the Add your data card follow the same rule for marketing texts: an sms_consent, sms_opt_in or Accepts SMS Marketing column is read for each new number, and without one a new number starts "Not granted" for marketing SMS and WhatsApp.
  • Scheduled file drops. The people in a drop become contacts when an audience finds them (below), so they start "Not granted"; a consent column in the drop file is not read for them.
  • Shopify. A new buyer whom Shopify has as subscribed to your e-mail marketing is recorded with that consent; any other status – not subscribed, pending confirmation, unsubscribed – or none starts "Not granted".
  • Stripe. A payment carries no marketing consent, so a new payer starts "Not granted".
  • Audiences built from your data. A person an audience finds in your warehouse – identified on your website, logged in, signed up – who was not yet a contact is added as "Not granted". So is an e-mail an audience adds to a contact you had only by your customer ID.

Someone already in your contacts keeps what they had, with one exception that matches your landing page's consent box: when the only thing stopping marketing to them is this "Not granted" – they never unsubscribed and never withdrew consent – a "yes" from your data (a file's consent column, Shopify's subscribed status, the API's consent: { email: true }) is recorded and they become reachable. A "no" in a later file, or a later Shopify status, is never read as a withdrawal, and an unsubscribe is never overridden. Consent they gave you before (on their consent history) is never undone when their record is created again.

What you can load

The warehouse takes more than orders. Anything you can export as a file has a home:

  • Events – anything that happened. Purchases, deposits, withdrawals, bets, wins, bonus grants, sign-ups. Include an event_type column and we keep your words: a withdrawal stays a withdrawal, and is never counted as revenue.
  • Customers or players – who they are. A roster with status, tier, KYC state, self-exclusion flag, lifetime value, and any other column you send. Re-sending the whole file updates people rather than duplicating them, so a nightly full export is exactly the right shape.
  • Catalogues – what they play or buy. Games, bonuses, products. Each catalogue is named, and its items carry whatever attributes you have.
  • Ad performance. A date, a platform, a spend per row.
  • Plans – your budget, forecast and target. Sent by your systems (see Sending plans above) or uploaded as a spreadsheet in the Flash Report's Plans.

Extra columns are never dropped. Whatever you send that we don't recognise is kept as an attribute, appears in the Data Manager, and can be used for segmentation.

Telling us what counts as revenue

We do not assume it. A deposit, a premium, a commission and a subscription are all somebody's revenue, and a payout carries an amount without being any of them – so guessing would mean quietly counting money you paid out as money you took in.

In Data → Events, each event type you send has a Money setting:

  • Counts as revenue – amounts on this event roll up into your revenue figures and ROAS.
  • Paid out – money going the other way. Never counted as revenue.
  • Not money – the event has an amount that is not a payment at all.

Anything left undeclared contributes nothing to revenue. That is deliberate: a missing number you can see beats a wrong number you cannot.

Subscriptions and MRR

If you sell subscriptions, give each payment event a detail that says how often it bills – for example billing_period with monthly or annual. The name is yours to choose; values such as quarterly, weekly, year or P1M work too. With it, your AI team – and chat, on plans with the data warehouse – calculates MRR, ARR and how MRR moved (new, expansion, reactivation, contraction and churned), paying customers and logo churn, by week, month or quarter.

  • A plan detail such as plan or tier splits MRR by plan.
  • An event whose name says cancel or churn, such as subscription_cancelled, marks a cancellation: the customer leaves MRR when the period they paid for ends.
  • A renewal up to seven days late is not counted as a churn and a comeback.
  • An annual payment counts as a twelfth of its amount each month.

Without an interval detail, MRR is not calculated: you get revenue, orders and paying customers instead, and the answer says why.

Sending the same file every day

Yes, and it does the right thing in each case – but the safest shape is worth knowing.

Best: include an id column. A transaction_id, bet_id or order_id makes each row permanently identifiable. Send a new day's transactions and they add; re-send a corrected version of yesterday's and each row replaces itself. Nothing double-counts, and correcting a single wrong amount is just re-sending the file with it fixed.

Without an id column, we identify a row by its contents. That still behaves correctly: a genuinely new day's rows are new, an accidental second send of the same file changes nothing, and re-sending a rolling window (say the last seven days, every night) updates those days rather than duplicating them – even if the rows come back in a different order. The one thing it cannot tell apart is two byte-identical rows in the same file; those are kept as two events, which is usually what you want for a repeated stake or a repeated purchase.

Customer and catalogue files are a snapshot, and that is fine. A full nightly roster updates the people in it and holds their current state. Segment movement does not depend on it: lifecycle stages are computed from your event history, so how customers move between segments is fully reconstructible from the events you send – no snapshots required, and re-sending a roster never disturbs it. What a roster does not give you is a history of your OWN attributes: if you want "player moved from silver to gold" to be visible as an event, send it as one.

Ad performance is keyed by day, platform and campaign. Re-sending a day replaces that day.

If the wrong data arrives

Nothing is frozen. Three ways to correct it, and each correction is recorded alongside the original load:

  • Re-send the file. A repeat delivery replaces that load rather than adding to it, so fixing a bad export is a matter of sending the good one. For customer and catalogue files this updates the records in place – a full nightly export is exactly the right shape.
  • Delete a single load. Every upload is listed under Data → Push with its row count and time; removing one takes its rows with it.
  • Clear a profile. Removes everything loaded for that business profile.

The activity log itself can never be edited or removed, including by an owner – it is the record of what happened, not the data.

Files that arrive on a schedule

A one-off CSV drops straight on the Push tab. An export that repeats – your nightly player file, a weekly finance extract – should be a feed, further down the same tab.

Give it a name and say how often it should arrive. From then on:

  • It remembers your columns. You map them once, on the first file. Every delivery after that is loaded the same way, so nobody re-maps the same export every night.
  • It tells you when a file doesn't turn up. This is the real reason to use a feed. A nightly export that quietly dies is wrong numbers for a week before anyone notices; a feed raises it the morning it happens.
  • It works by hand or by machine. Drop a file on the feed here, or have a scheduled job send it with an ingestion key – same feed either way, and the history shows which was which.

If you'd rather not commit to a schedule, leave it on Detect the rhythm. We watch the first few deliveries, work out the pattern, and offer it back to you to confirm – we never invent a schedule you didn't agree to. Pausing a feed stops the overdue alerts without deleting anything.

Activity. Underneath is the record: every file loaded, every batch received, every mapping changed, every deletion – who or what did it, from which address, how many rows moved, and whether it worked. Failures are recorded too, so "the nightly load stopped three days ago" is a question you can answer. Nothing can edit or remove an entry, including an account owner.

Giving your data team their own access

If the people who wire up your systems are not the people who run your marketing, invite them as a Data engineer.

That seat sees the Data page – sources, connection, keys, activity, the event catalog and the warehouse – and nothing else. No campaigns, no journeys, no audiences, no billing. They can load data, map it, correct a bad file, and issue their own keys, without ever seeing the marketing side of the account.

To be precise about one thing, because it matters for regulated businesses: they can see the data they load, and that includes customer identifiers, because inspecting it is the job. What they cannot do is act on it – no campaign, no journey, no export to an ad platform, no message to anyone.

Two things they deliberately cannot do: erase all data for a business profile, and see anything outside the Data page. Purging a profile stays with owners and admins.

Invite them from admin.theaicmo.com → Team, choosing Data engineer as the role. It takes a seat like any other member.

Your website

The Website SDK is one script tag that brings visits, conversions, and revenue from your site into the warehouse – and it's how website messages from Journeys reach your pages. It has its own manual: Website SDK. The Push tab shows its status at a glance, and the SDK page itself flips to "Receiving data" the first time an event arrives.

Your event vocabulary

The Events tab is where you define the business moments that matter – a deposit, a booking, a quote request. Register an event name and you get three ready-to-paste ways to capture it: a JavaScript call, an HTML attribute, or a server webhook. You can also set up no-code capture – point the event at a button, form, or URL on your site and the SDK fires it without any code change, even reading the value straight off the page.

Two details worth knowing:

  • Events your site sends that aren't registered yet show up as seen from your site, not registered – one click registers them.
  • An event can be marked as a suppression event: anyone who fires it is blacklisted from all contact. Built for self-exclusion and do-not-contact flows.

Registered events appear in the Warehouse tab as they stream in, can trigger journeys ("someone fires this event" enrolls them within about a minute), and can define audience segments.

Included events and what happens at the ceiling

Every plan includes a set volume of warehouse events – one event is one order, one deposit, one conversion, one webhook push. The meter on Data → Warehouse names the number for your account ("Included events – X of Y on your plan") and turns amber as you approach the ceiling. Your plan page lists the volume that comes with each plan, and that page is the one to trust – it is the number the import checks against. If you expect to load more history than your plan includes, tell us before you start and we will size it with you.

The rules at the limit are deliberately one-sided:

  • CSV imports pause – an import that would pass your included volume is declined with the exact numbers, and your file is untouched. Upgrade, or trim the file, and import again.
  • Live streams never stop. Website events, webhooks, and store syncs keep flowing – rejecting live data would destroy it, so we don't.

You get a bell notification at 80% and at 100%.

Higher plans include a warehouse of your own on our ClickHouse service, which you switch on yourself under Data → Warehouse; it lifts the file-size limit on imports and is built for far larger books. It still has an included volume. Capacity beyond that – a cluster sized to millions of events a month – is arranged with us.

How to know it worked

  • A CSV import confirms immediately and tells you where to look – purchases light up the Overview numbers on the spot.
  • A contact import names the segment it created in Customer Intelligence → Audiences.
  • The Website SDK page tells you where the install stands: "Installed – the tag is on your site" once the snippet is found in your page, "Script loaded" when the script reports in by itself (a tag manager, most likely), and "Receiving data" with your page views once events arrive. Check again re-reads the site on the spot.
  • The Warehouse tab lists every event type the moment it streams in – including your registered custom events.