What is Seb?

Seb is Coopah's AI junior partner account manager. When a new event partner is introduced via Slack, Seb handles the full onboarding flow autonomously: researching the partner, creating affiliate codes, building a Notion workspace, drafting and sending outreach, chasing replies, reporting performance, and managing ongoing campaign communications.

Seb is designed to handle all the repeatable, process-driven parts of partner management so the human AM team can focus on relationships and decisions that require real judgment.

Design Principles

Who uses it?

The Coopah Account Management team. Seb is triggered from Slack. The AM team reviews email drafts on the partner dashboard, handles escalations, and monitors weekly summaries.

Architecture

Seb Agent
Railway · Node.js
Slack
Socket Mode
Rules Engine
Vercel · Next.js
Coopah Admin
Playwright
+
AppFlyer
Playwright / MCP
+
Notion
SDK
+
Gmail
OAuth2
+
Mixpanel
REST API
ComponentHostPurpose
Seb AgentRailway (always-on)Orchestrates all flow steps, runs Playwright, calls Claude
Rules EngineVercel (Next.js)Config UI — team edits behaviour without touching code
Slack listenerPart of AgentSocket Mode WebSocket — receives mentions, slash commands, modal submissions

Entry Points

@seb manage [partner name]

Mention @seb manage Royal Parks Half Marathon in any Slack channel to start the full onboarding flow. Seb researches the partner, posts findings to the intros channel, and waits for the check-in modal to be submitted before proceeding.

/coupon

Standalone tool for creating discount codes outside of the onboarding flow. See Seb Tools → /coupon.

Onboarding Flow

Triggered by @seb manage [partner name]. Eight sequential steps — click any step to expand the detail.

1

Research + Check-in

Web research → Slack post with findings → modal collects partner details

Research

Seb calls Claude with the web_search_20250305 tool to find: full event name, next upcoming date (YYYY-MM-DD), approximate participant count, race type, official website. Today's date is passed so Claude searches for the next future occurrence.

Research findings are posted to config.escalation.introsChannel as a Block Kit message, tagging the AM handle with a "Fill in details" button. If research fails, Seb proceeds with confidence: low and the team corrects via modal.

Check-in Modal

Clicking "Fill in details" opens a Slack modal. Seb pre-fills what it found; the AM confirms or corrects:

FieldTypeRequiredNotes
Contact emailText inputYesPartner contact email
Contact nameText inputNoFor personalised emails
OfferDropdownYesFrom config.offers.items (enabled only). Default = config.offers.default
Coupon codeText inputYesMax 15 chars. Pre-filled: name uppercased + non-alphanumeric stripped + "2". Human has final say.
NotesMultilineNoAny extra context before Seb reaches out

On submit, the confirmed data is passed to all subsequent steps.

2

Coupon Creation

Playwright creates the discount code in the Coopah admin

Seb logs into admin.app.coopah.com via headless Playwright and creates the coupon using the code confirmed in the check-in modal.

FieldValue
TitleSame as coupon code
Coupon codeAs confirmed in Step 1 (max 15 chars)
Apple codeoffer.appleCode
Google codeoffer.googleCode
Media sourceAlways Event Partners for onboarding flow
Campaign nameCODE_OfferLabel_EventPartners_MonthYear

On success, Seb replies in thread: ✅ Coupon created: `CODE`. On failure, Seb escalates to Slack with full error details and halts.

3

AppFlyer Link

Creates a tracking OneLink in AppFlyer for the partner's campaign

Seb creates the AppFlyer OneLink tracking link via the OneLink REST API v2. This is non-blocking — if the API call fails, Seb escalates with full details for manual creation and continues the flow.

API call

POST to https://onelink.appsflyer.com/api/v2.0/shortlinks/{templateId} with the following parameters:

ParameterValue
pidevent_partners
cCampaign name (CODE_OfferLabel_EventPartners_MonthYear)
af_dpThe coupon code (used as deep link value)

Environment variables required

VariablePurpose
APPSFLYER_API_TOKENBearer token for the OneLink REST API
APPSFLYER_ONELINK_TEMPLATE_IDThe template ID used in the endpoint path
If the AppFlyer API call fails for any reason, Seb posts the full link parameters to the escalation channel for manual creation and continues to Step 4. The OneLink URL is stored in the session when available.
4

Partner URL

Generates the partner's URL on the rules engine

The Notion Hub approach has been replaced by a partner page hosted directly on the rules engine (Vercel) at /partners/[slug]. There is no external Notion dependency.

The partner URL (https://meetseb.co/partners/[slug]) is stored in the session and included in all subsequent emails and Slack messages.

Page contents

  • Partner info (name, event, contact details)
  • Coupon code
  • AppFlyer OneLink
  • Email drafts
  • Activity log
5

Mixpanel Report Setup

Duplicates the base Mixpanel template report, scoped to this partner's coupon code

During onboarding, Seb creates a dedicated Mixpanel report for the partner so performance tracking is ready from day one — before any emails have been sent.

Process

  1. Find the base template report (created by Dan, referenced in config.reporting.mixpanelTemplateId)
  2. Duplicate the report via the Mixpanel API
  3. Rename it to the partner's campaign name
  4. Update the discount code filter to match this partner's coupon code
  5. Save the report URL to the partner's Notion hub (Performance section) and session

This report is used for: the partner-facing weekly performance email, the internal AM dashboard, and Seb's trial stats notifications.

If Mixpanel duplication fails, Seb logs the failure and escalates with a link to the base template and the correct coupon code so the report can be created manually.
6

Email Drafting

Claude drafts the intro email using the base template, customised for this partner

Claude drafts the introduction email using config.templateLibrary[0].notionUrl as the base, customised for the specific partner. The draft is stored in Vercel KV and immediately surfaced on the partner's internal dashboard page for review — the AM does not need to leave the rules engine to read or edit it.

Draft must include

  • Introduction line from config.identity.introLine
  • Link to the partner's public hub page
  • Proposed email schedule (calculated from race date)
  • Affiliate/coupon code
  • AppFlyer tracking link
  • Asset request (if config.onboardingSteps.ob6 enabled)
  • Social nudge (if config.onboardingSteps.ob8 enabled)
  • AI disclosure (if config.identity.discloseAI enabled)

Tone and sign-off taken from config.identity.tone and config.identity.signOff.

7

Approval Gate (first email only)

AM reviews and edits the draft on the partner page — approve when ready

The intro email is the only email that requires explicit approval. After the AM approves the first email for a partner, all subsequent emails are sent autonomously — escalation handles exceptions.

Review happens on the partner's internal dashboard page in the rules engine — not via Slack. Seb posts a Slack notification with a direct link when the draft is ready.

Editor

The draft is rendered as an inline editable document on the partner page. The AM can:

  • Edit directly — click into any part of the subject or body and type. For small fixes (a word, a sentence) this is the fastest path. Changes are saved automatically to Vercel KV.
  • Ask Seb to rewrite a section — select any text, click "Ask Seb", type a brief note (e.g. "too formal", "mention the marathon distance"). Seb rewrites just that selection and updates the draft in place. The AM can accept or undo.
  • Approve — hits the Approve button when satisfied. Seb proceeds to send immediately.

If the draft needs a full rewrite

The AM can use the "Regenerate" button with overall notes, which prompts Seb to redraft the whole email. Maximum 3 full regeneration attempts before Seb escalates to Slack as HIGH severity and halts.

On approval

Approval is timestamped and logged in the partner's comms history in Vercel KV. The final sent version of the email is also stored.

8

Send + Chase

Email sent via Gmail, inbox monitored, chase loop runs if no reply

Seb sends the email via Gmail OAuth and monitors the inbox for a reply.

EventAction
Reply receivedParse intent → move to Campaign Management
No reply after chaseDaysSend brief chase email (Template 2), repeat up to maxChaseAttempts
No reply after max chasesEscalate HIGH severity, halt
Partner agrees to scheduleUpdate Notion hub to "Agreed", post success to Slack, enter campaign management loop
Partner asks questionsEscalate with full context + question, pause
Partner pushbackEscalate HIGH immediately, pause

Timing: config.emailSchedule.chaseDays, config.emailSchedule.maxChaseAttempts.

On agreement, the onboarding flow is complete. Seb enters the ongoing Campaign Management loop for this partner.

Campaign Management

Once a partner has agreed to the email schedule, Seb manages the ongoing relationship autonomously. This covers all touchpoints from first agreed email through post-event.

Pre-send Check-in

Before each scheduled email or social post send date, Seb runs a check-in to confirm everything is in order:

If all clear, Seb proceeds automatically. If any issue is found, it escalates to Slack before sending.

Post-email Results Collection

Once a partner confirms they've sent an email or social post to their audience, Seb follows up to collect performance data.

Trial Stats (3-day post-email)

Three days after each send date, Seb automatically queries Mixpanel and sends a brief stats notification.

This data informs the weekly report and Seb's content recommendations over time.

Responding to Partner Requests

Partners may reply with questions, requests, or changes outside the standard flow. Seb parses the intent and responds where confident.

Request typeSeb's action
Standard question (in knowledge base)Seb responds autonomously, logs in Notion comms
Non-standard / unusual requestEscalate to Slack with full thread context + draft response for AM approval before sending
Request to change scheduleEscalate with proposed new schedule for AM sign-off
Complaint or sensitive matterEscalate HIGH immediately, do not respond until approved
Rule: Any reply that doesn't clearly fit a known pattern is escalated rather than guessed at. Seb defaults to escalation when uncertain.

Weekly Performance Report (Every Monday)

Every Monday, Seb sends an automated performance summary to each active partner contact.

Configurable: config.reporting.cadence, config.reporting.day, config.reporting.includeOpenRate, config.reporting.includeConversionRate.

Email System

Templates

6 base templates stored as Notion pages, referenced by URL in config.templateLibrary. Claude fetches base content and customises per partner.

#NamePurposeSent when
1IntroductionFirst outreach emailAfter approval gate (one-time)
2ChaseNo-reply follow-upAfter chaseDays with no reply
3Schedule confirmedConfirms agreed email planAfter partner agrees
4Pre-event reminderReminds partner of upcoming sendPer schedule
5Performance reportMixpanel stats for their campaignWeekly (Monday)
6Post-event wrapThanks + next stepsAfter event date

Email Schedule by Race Type

Race typeEmail 1Email 2Email 3Email 4
Marathon16 weeks out12 weeks out8 weeks out4 weeks out
Half marathon12 weeks out8 weeks out6 weeks out3 weeks out
10k10 weeks out6 weeks out4 weeks out2 weeks out
5k / parkrun8 weeks out4 weeks out2 weeks out1 week out

Notion Hub Structure

Every partner gets a Notion page under config.notionConfig.partnerHubsParentPageId, titled [Partner Name] — [Event Name].

SectionContents
Quick ReferenceCoupon code, AppFlyer link, race type, race date, participant count, website, contact name + email, Mixpanel report link
Email ScheduleTable: email number, template, planned send date, status (Planned / Agreed / Sent / Confirmed)
Email DraftsSubpages — one per template — with subject, preview text, body, send date, status
Partner AssetsGoogle Drive folder link, received status
SocialSocial caption draft, agreed status
PerformanceMixpanel report link, email stats (open rate, click rate) per send, trial counts, last report date
Notes & Comms LogChronological append-only log — every Seb action, email sent, reply received, escalation raised

Internal AM Dashboard

Built into the rules engine on Vercel. Two views — an overview of all partners, and a per-partner detail page that also serves as the email draft editor.

/partners — All partners overview

A card per active partner showing at a glance:

/partners/[slug] — Individual partner page

The full partner view. Also serves as the email draft editor during the approval gate:

Data source: All partner state is in Vercel KV. Mixpanel stats fetched on demand. No Notion dependency for the dashboard — Notion is email templates only.

Escalation Rules

All escalations are posted to config.escalation.channel, tagging config.escalation.amHandle. Severity determines emoji and urgency.

TriggerSeverityAction
Coupon creation failsHighPost error + details, halt flow
AppFlyer automation failsMediumPost link params for manual creation, continue
Intro email not approved after 3 revisionsHighPost draft + history, halt
Non-standard partner requestMediumPost request + draft response, await AM approval before sending
Partner asks unanswerable questionLowPost question + full thread context, pause
Partner pushbackHighPost pushback text + history, pause immediately
No reply after max chasesHighPost contact details + full history, halt
Pre-send check-in finds an issueMediumPost issue detail, await AM go-ahead before sending
Mixpanel report creation failsMediumPost template + coupon code for manual setup, continue
Unhandled errorHighPost error + stack trace, halt

Data & Reporting

Mixpanel Performance Reports

Seb maintains a dedicated Mixpanel report per partner (created at onboarding, Step 5). Reports are filtered by affiliate_code = [coupon_code].

Email Performance Tracking

When a partner reports open/click rates for their own email, Seb stores these in Notion and surfaces them in reports. Seb tracks patterns across all partners to identify what subject lines, send days, or offer types generate the best engagement.

Data Storage

All partner data — session state, email drafts, comms logs, schedule, escalation history — lives in Vercel KV. Notion is used only for the 6 base email templates (human-edited). Mixpanel is the source of truth for trial and conversion data.

Seb Learns

Seb tracks outcomes across all partners and uses this to improve future work. This is not a separate system — it's built into Claude's context when drafting emails or analysing performance.

Implementation note: In the first version, learning state is stored as structured notes in Vercel KV and passed as context to Claude. A more sophisticated embedding-based retrieval layer may be added later.

Seb Tools

Standalone capabilities that can be invoked directly from Slack, independent of the onboarding or campaign management flow.

/coupon — Standalone Coupon Creator

Type /coupon in any Slack channel to open the coupon creation modal. This is a utility tool for creating discount codes outside of any partner onboarding — useful for ad hoc campaigns, influencer codes, or testing.

Modal fields

FieldNotes
Coupon codeMax 15 chars. Free text — no pre-fill.
OfferDropdown from config.offers.items (enabled only)
Media sourceDropdown: Event Partners, Coopah Emails, Brand Partners, Tier 1 Influencers, Tier 2 Influencers

On submit

Seb creates the coupon in the production Coopah admin (https://admin.app.coopah.com) using the provided values. Campaign name: CODE_OfferLabel_MediaSource_MonthYear. Result is sent back to the user via DM: ✅ Coupon created: `CODE` or an error message.

This command uses the production admin URL regardless of the ADMIN_TEST_URL environment variable.

Authentication

The rules engine (www.meetseb.co) and the internal AM dashboard (/partners) must be protected. These pages can change Seb's behaviour and contain partner data — they should not be publicly accessible.

Magic link (email)

Authentication is handled via magic link — no passwords. The flow:

  1. User visits any protected page and is redirected to /login
  2. They enter their email address
  3. If the email is in the whitelist (config.auth.allowedEmails), a time-limited signed link is emailed to them
  4. Clicking the link sets a secure HTTP-only session cookie (JWT, 7-day expiry)
  5. They're redirected to the page they originally requested

What is protected

PathProtectedNotes
/ (rules engine config)YesMagic link required
/system-promptYesMagic link required
/partners and /partners/[slug]YesMagic link required
/api/config (GET)NoRead-only, needed by Railway agent on startup
/api/save (POST)YesRequires valid session
/spec.html, /plan.htmlNoPublic — documentation only
/loginNoThe login page itself is public

Implementation

Note: /api/config (GET) is intentionally left unprotected so the Railway agent can fetch config on startup without needing a session. It only returns config data — no partner PII. The save endpoint requires auth.

Configuration Reference

All config is managed via the Rules Engine UI and stored in Vercel KV. The agent fetches it on each run via SEB_CONFIG_URL.

identity

KeyDescription
identity.nameSeb's display name in emails + Slack
identity.emailFrom address for outbound emails
identity.introLineOpening line in every intro email
identity.toneVoice for Claude: friendly / professional / casual
identity.signOffEmail sign-off line
identity.discloseAIIf true, Claude adds AI disclosure to emails

onboardingSteps

ToggleControls
ob1Coupon creation + AppFlyer link
ob2Notion hub creation
ob3Email template customisation + schedule calculation
ob4Intro email drafting
ob5Send email + chase loop + reply processing
ob6Include asset request in intro email
ob7WordPress blog post creation
ob8Include social nudge in intro email
ob9Post-event wrap-up email

offers

KeyDescription
offers.defaultPre-selected offer in check-in modal
offers.items[]Each: label, appleCode, googleCode, enabled

emailSchedule

KeyDescription
emailSchedule.timingObject keyed by race type → array of weeks-out numbers
emailSchedule.chaseDaysDays before sending chase email
emailSchedule.maxChaseAttemptsMax chases before escalating

escalation

KeyDescription
escalation.channelSlack channel for escalations (e.g. #seb-escalations)
escalation.introsChannelChannel for check-in messages (e.g. #seb-intros)
escalation.amHandleSlack handle to tag (e.g. @dan)

notionConfig

KeyDescription
notionConfig.partnerHubsParentPageIdParent Notion page ID for all partner hubs
notionConfig.enabledToggle Notion integration on/off

affiliateCode

KeyDescription
affiliateCode.linkNamingTemplate for AppFlyer link name, e.g. COOPAH-[PARTNER]-[YEAR]
affiliateCode.appFlyerMethodplaywright | mcp | zapier | manual

templateLibrary

Array of 6 template objects. Each: id, name, desc, notionUrl, requiresApproval.

reporting

KeyDescription
reporting.cadenceweekly / monthly / manual
reporting.dayDay for weekly reports (default: Monday)
reporting.includeOpenRateInclude email open rate in partner reports
reporting.includeConversionRateInclude Mixpanel conversion rate
reporting.slackSummaryPost weekly digest to Slack
reporting.slackChannelChannel for internal Slack digest
reporting.mixpanelTemplateIdMixpanel report ID to duplicate for each new partner

Integrations

IntegrationHowUsed for
Slack@slack/socket-mode + @slack/web-apiTriggers, modals, approvals, escalations, notifications
Coopah AdminPlaywright (headless Chromium)Coupon creation
AppFlyerPlaywright / MCP / Zapier (TBD)OneLink tracking link creation
Notion@notionhq/client SDKPartner hub creation, template fetching, comms log
Gmailgoogleapis OAuth 2.0Send emails, poll replies
MixpanelREST APITrial + conversion data for reports
Claude (Anthropic)@anthropic-ai/sdkResearch, drafting, reply parsing, reporting narrative

Environment Variables

Railway (Agent)

VariableDescription
ANTHROPIC_API_KEYClaude API key
SLACK_BOT_TOKENxoxb-... — bot token for posting messages
SLACK_APP_TOKENxapp-... — app-level token for Socket Mode
ADMIN_EMAILLogin for admin.app.coopah.com (production)
ADMIN_PASSWORDPassword for Coopah admin (production)
ADMIN_TEST_URLOptional — overrides admin URL for staging/testing
ADMIN_TEST_EMAILOptional — login for staging admin
ADMIN_TEST_PASSWORDOptional — password for staging admin
NOTION_API_KEYNotion integration token
GMAIL_CLIENT_IDGoogle OAuth client ID
GMAIL_CLIENT_SECRETGoogle OAuth client secret
GMAIL_REFRESH_TOKENLong-lived OAuth refresh token (generated once)
MIXPANEL_SERVICE_ACCOUNTMixpanel service account username
MIXPANEL_SERVICE_SECRETMixpanel service account secret
MIXPANEL_PROJECT_IDMixpanel project ID
SEB_CONFIG_URLRules engine config endpoint, e.g. https://www.meetseb.co/api/config. Falls back to local seb-config.json if unset.

Vercel (Rules Engine)

VariableDescription
MAGIC_LINK_SECRETSecret for signing magic links and session JWTs. Generate with openssl rand -hex 32.
AUTH_ALLOWED_EMAILSComma-separated list of emails allowed to log in, e.g. dan@coopah.com,alice@coopah.com
KV_REST_API_URLSet automatically by Vercel Upstash integration
KV_REST_API_TOKENSet automatically by Vercel Upstash integration
KV_REST_API_READ_ONLY_TOKENSet automatically by Vercel Upstash integration

Vercel (Rules Engine) — other auto-set vars

VariableDescription
KV_URLVercel KV connection URL
KV_REST_API_URLVercel KV REST API URL
KV_REST_API_TOKENVercel KV REST API token
KV_REST_API_READ_ONLY_TOKENRead-only token

Scope & Limitations

Out of scope (v1)

Known constraints