MICROMARKETING Book With Tony
← AI & Automation

Build a ChatKit Campaign-Brief Assistant That Respects Suppression Lists

In May 2026, I watched a good growth team nearly brief the wrong audience because their AI helper treated "high intent" as the only rule that mattered.

In May 2026, I watched a good growth team nearly brief the wrong audience because their AI helper treated “high intent” as the only rule that mattered. The model pulled demo visitors from Segment, noticed repeat pricing-page views, and suggested a HubSpot nurture plus a LinkedIn retargeting list. Sensible on paper. The problem sat one field away: 18% of those contacts had either opted out of the relevant HubSpot subscription type or had no explicit opt-in under the account’s data privacy settings.

That is the marketing-ops problem ChatKit makes easier to expose, not easier to ignore.

OpenAI’s ChatKit gives teams a ready embedded chat surface. The React package, @openai/chatkit-react, wraps the openai-chatkit web component with a ChatKit component and a useChatKit hook. The quickstart shows the basic shape: install the package, point it at a server endpoint like /chatkit, and render a chat frame with a fixed height and width. ChatKit options include widgets, thread history, client tools, custom actions, themes, commands, and the usual event hooks like onResponseStart, onResponseEnd, and onError. That saves weeks if you were about to build chat chrome, history, streaming, and interactive cards from scratch.

But a prettier internal assistant can still do dumb things at scale.

For campaign work, the assistant cannot be allowed to answer from a warehouse snapshot alone. A useful campaign-brief assistant has to ask three boring questions before it writes a clever brief. Who is this person or account in Segment? What audiences, traits, and events are attached to that profile right now? What does HubSpot say about consent for the exact channel and subscription type the team is about to use?

Segment’s Profile API is built for the first part. Its docs describe a server-side API for reading user-level and account-level profiles, including external_ids, traits, events, and audiences. Segment says Profile API can fetch traits in under 200ms and query by identifiers such as user_id, advertising IDs, anonymous_id, or custom external IDs. It also warns against calling the API directly from client applications. That one line matters. Your ChatKit UI should never be the thing holding Segment tokens or deciding whether a suppressed audience can slip through.

HubSpot handles the second hard line. Its April 7, 2026 subscription-type docs say each subscription type creates a matching “Opted out of email: [Email Subscription Type]” contact property, and its contact UI can show three states: Subscribed, Unsubscribed, or Not specified. With data privacy settings turned on, HubSpot maps those to opted in, not opted in or out, and opted out. HubSpot also says contacts in the default neutral state cannot be emailed until explicit consent is collected when the account enforces that rule. That is not a nuance. It changes who the assistant is allowed to recommend.

The design I trust is simple. ChatKit is the conversation layer. A backend policy service is the authority. Segment and HubSpot are inputs, not suggestions.

In a real build, I would give the assistant a narrow tool called draft_campaign_brief, not broad warehouse access. The tool takes a business question like “Win back stalled trials from the last 45 days” and returns a policy-checked object: audience size before suppression, audience size after suppression, channel eligibility, blocked reasons, sample Segment traits used, HubSpot subscription IDs checked, and a brief draft. The model can write copy around that object. It cannot invent the object.

That split prevents the common leak. The leak usually does not look like a CSV export named suppressed_contacts.csv. It looks like a Slack message that says, “Try a reactivation email to pricing-page visitors who stopped after trial day 10.” Someone builds the list fast. Nobody notices that the assistant made a channel recommendation against people with UNSUBSCRIBED or NOT_SUBSCRIBED status in HubSpot’s communication preferences API.

The safe flow starts with identity resolution. When a marketer asks for “enterprise leads who viewed pricing twice,” the backend queries Segment Profiles by the IDs your system already trusts. For B2B, I like account-first resolution: account_id, then known users, then anonymous IDs linked through Segment Identify calls. Segment’s Identify spec recommends sending an Identify call after registration, login, or profile updates, and every call needs either a User ID or an Anonymous ID. If those links are sloppy, the assistant should say the audience is not briefable yet.

Next comes audience construction. Segment can tell you traits and audiences, but your policy service should name the fields it used. A campaign brief that says “high-intent accounts” is too vague. A usable one says it selected accounts with plan = team, employees >= 50, two Viewed Pricing events in 14 days, and no Opportunity Created event in Salesforce after July 1, 2026. Those field names should appear in the ChatKit widget before anyone clicks approve.

Then consent filtering runs. For email, call HubSpot communication preferences by email address or pull the relevant contact properties through your existing sync. HubSpot’s Subscription Preferences V3 endpoint returns subscription statuses for an email address, and the 2026 V4 docs list OAuth scopes such as communication_preferences.read, communication_preferences.write, and batch read or write scopes for Enterprise accounts. I would cache statuses for minutes, not days. Consent is one of those fields that feels static until a customer unsubscribes 30 seconds before a send.

The rule table should be dull enough for counsel to read.

For a product newsletter, require SUBSCRIBED for the matching HubSpot subscription type when data privacy settings are on. Treat UNSUBSCRIBED as blocked. Treat NOT_SUBSCRIBED or Not specified as blocked for marketing email under consent-based sending. For paid social custom audiences, check your own policy against consent, region, source, and platform terms before the assistant recommends upload. For sales one-to-one email, use the specific HubSpot subscription type and legal basis your revops team has approved. Do not let the model make channel law out of vibes.

ChatKit widgets are useful here because the assistant can show the decision, not bury it in prose. OpenAI’s ChatKit widget guide describes prebuilt and custom widgets, card layouts, list rows, uploaded assets, and custom action payloads sent back to the application. In this assistant, I would use a widget with four fixed rows: Requested audience, Eligible audience, Suppressed audience, and Channel recommendation. If the requested audience is 12,480 profiles and 2,210 are blocked by HubSpot email status, the user should see 10,270 eligible before reading the copy angle.

I like adding a “why blocked” drawer. It does not show raw emails unless the operator has permission. It shows counts: 1,420 unsubscribed from Product Updates, 620 not specified under data privacy enforcement, 170 missing HubSpot contact match. In one April 2026 audit, those three buckets explained 97% of the suppression delta for a Series B SaaS company sending to about 84,000 contacts. That was enough for the lifecycle manager to fix forms and preference-center mapping without exposing the people inside the blocked list.

Approval needs the same discipline. ChatKit supports custom actions through client and server handlers. Use that for “Create draft in HubSpot” or “Send to Asana for review,” but make the server rerun policy checks when the action fires. The first answer and the approval click may be five minutes apart. A contact can unsubscribe during those five minutes. A teammate can change a subscription type. Your system needs to check again.

The prompt matters less than the contract, but it still matters. I would tell the assistant, in plain language, that it may not recommend a channel unless the policy service returns eligible: true for that channel. I would also ban it from estimating suppressed counts. If Segment or HubSpot is unavailable, the answer should be blocked with a specific reason like hubspot_status_timeout or segment_profile_missing, not softened into “you may want to verify compliance.” Operators are busy. The assistant has to stop the workflow when the data is missing.

There is a useful product choice here: make the assistant less charming. The first version should refuse a lot. If a founder asks for “a quick campaign to everyone who visited pricing,” the assistant should come back with a smaller eligible audience and the reason. Something like: “I found 4,812 matching profiles in Segment. I can brief 3,906 for Product Updates email. I blocked 906 because HubSpot shows 441 unsubscribed, 388 not specified, and 77 with no matching email subscription status.” That is not glamorous, but it is what keeps the list clean.

Organic recommendations need the same gate in a lighter form. If the assistant suggests a LinkedIn organic post, no person-level consent check is needed for publishing the post. If it suggests inviting a suppressed customer cohort to comment, join a webinar, or receive a DM, the gate is back. Channel labels help. I use broadcast_public, paid_audience, marketing_email, sales_email, and in_product as policy categories because each one has a different risk profile and evidence trail.

Logging is the part teams skip until the first incident. Store the prompt, resolved Segment audience definition, HubSpot subscription IDs checked, counts before and after suppression, policy version, model ID, approver, and timestamp. Keep the generated prose too, but treat it as secondary. Six months later, nobody will care that the subject line had a nice hook. They will ask why 312 people received a win-back email after opting out on March 12, 2026.

A small team can ship this without turning it into a platform project. ChatKit handles the embedded UI. Segment Profiles handles the identity graph and audience context. HubSpot handles subscription truth. The backend policy service can start as one TypeScript module with a dozen tests and a Postgres audit table. The important boundary is that every brief, segment idea, and channel recommendation must pass through the same gate before it becomes work.

AI campaign assistants are going to become normal inside marketing teams in 2026. The teams that get value from them will not be the ones with the longest prompt library. They will be the ones that wire the assistant into the systems that already decide who can be contacted, why they qualify, and which channels are off limits today.

Keep reading

Continue with practical guidance from the same topic collection.