Skip to content

Spec: Workflows first run #256

Closed
Closed

Description

@Silthus

Map: Map: build the Workflows first run, from template gallery to first send
Answers: Spec the Workflows first run and slice it into PRs
Built on: the accepted direction in the #212 handoff (round 9), the prototype README at products/workflows/frontend/prototype/first-run/README.md on prototype/workflows-first-run, and Spec: sandbox sender.

This issue is the reference document for the implementation tickets. It is closed on purpose so it stays off the frontier.

Problem Statement

A new team that opens Workflows sees a generic empty state, and "New workflow" drops it into a blank editor.
Before anything leaves, it has to learn triggers, connect a sender, verify DNS and write an email.
The template gallery exists, but the empty state skips it, and it does not know which templates the project's events can drive.
Most teams stop before a first message is delivered.

Solution

Behind one new feature flag, workflows-first-run, a project with no workflows gets one straight path on the Workflows tab:

  1. Tailored gallery. The real global templates that send email, each card showing only what starts it. The ones the project's events can drive come first, led by one recommended starter with a one-line reason. "Start playing" is the last card and opens a blank workflow.
  2. Make it yours. One screen with the real email editor, "Change with PostHog AI", "Send me a test", and "Enable and open workflow". Two switches sit under the button, both on: "Enable workflow" and "Capture engagement events".
  3. Enable. The workflow opens in the editor. As a draft it says it is not sending yet and points at Enable.
  4. Metrics. Once enabled it says it is sending and leads to the Metrics tab. It offers "Enable engagement events" while they are off.

When the project lacks a signup event or people's emails, a banner hands the fix to an agent: "Copy prompt for your coding agent" and "Let the Wizard do it".
First sends go from the first-run sender, one seam that resolves to the sandbox sender (#225) until the team has its own verified sender.
Who those first sends may reach is open in #254. Only the copy that names the recipients and the own-domain line wait on it.

User Stories

  1. As a new Workflows user, I want the Workflows tab to show email templates instead of an empty list, so that I start from something that works.
  2. As a new Workflows user, I want each template card to show only what starts it (an event, a stretch without an event, or a schedule), so that I can judge it at a glance.
  3. As a new Workflows user, I want the templates my events can drive listed first, so that I do not pick one that can never fire.
  4. As a new Workflows user, I want one recommended starter, highlighted and first, with a one-line reason, so that I do not have to choose.
  5. As a new Workflows user, I want to see every email template too, each with what it still needs, so that I know what to add to my app.
  6. As a new Workflows user, I want a "Start playing" card last, so that I can open a blank workflow when no template fits.
  7. As a new Workflows user, I want a template that triggers on signups to use the signup event my app already sends, so that it works without renaming my events.
  8. As a new Workflows user, I want one screen to make the template's first email mine, so that I reach a working email without learning the canvas.
  9. As a new Workflows user, I want the real email editor on that screen, so that what I edit is what gets sent.
  10. As a new Workflows user, I want to ask PostHog AI to change the email, so that I can rewrite it in plain words.
  11. As a new Workflows user, I want "Send me a test" to send the email I see to my own inbox in one click, so that I see it in a real inbox.
  12. As a new Workflows user, I want a blocked or skipped test to say why, so that I know what to change.
  13. As a new Workflows user, I want "Enable and open workflow" to create the workflow already sending, so that the first email goes out with no further step.
  14. As a careful Workflows user, I want to switch "Enable workflow" off and get a draft instead, so that nothing sends until I am ready.
  15. As a Workflows user, I want every email step of the new workflow to use a sender that works today, so that no step fails for lack of a sender.
  16. As a Workflows user, I want "Capture engagement events" on by default, so that my email events show up in insights and funnels.
  17. As a project member who is not an admin, I want the engagement switch disabled with a reason, so that I am not offered a change I cannot save.
  18. As a Workflows user, I want a draft first workflow to say it is not sending yet and point at Enable, so that I know the one step left.
  19. As a Workflows user, I want a sending first workflow to say so and lead to the Metrics tab, so that I see what it sent.
  20. As a Workflows user whose engagement events are off, I want the sending banner to offer "Enable engagement events", so that I can turn them on where I notice the gap.
  21. As a Workflows user, I want the first-run banner to tell me who the workflow emails and from which address, so that I am not surprised by who receives it.
  22. As a Workflows user without my own domain, I want a line that leads to setting it up, so that I know how to send from my own address.
  23. As a Workflows user whose app sends no signup event, I want a ready prompt for my coding agent, so that I can fix it without writing the code myself.
  24. As a Workflows user whose people have no email property, I want a ready prompt that adds it, so that my emails reach people.
  25. As a Workflows user, I want "Let the Wizard do it" to show the one setup agent command, so that the agent can make the change for me.
  26. As a new organization, I want the Workflows quick start to follow the first run (test, enable, own domain), so that it does not send me to DNS first.
  27. As a PostHog product manager, I want events for gallery shown, template picked, test sent and workflow created, so that I can see where the first run loses people.
  28. As a team whose project already has workflows, I want the Workflows tab unchanged, so that the first run never gets in my way.

Implementation Decisions

Vocabulary

  • First run: the path from an empty Workflows tab to a first workflow, behind workflows-first-run.
  • Gallery template: a global workflow template with at least one function_email step.
  • Starts on: what starts a gallery template: an event, a no_event (a stretch without that event), or a schedule.
  • Fit: whether the project can drive a gallery template today. A ready template has a matched event; a template that is not ready names what it still needs.
  • Recommended starter: the one ready template the gallery puts first.
  • First-run sender: the email sender the first run puts on every email step and on "Send me a test". One seam.
  • First-run workflow: the workflow "Enable and open workflow" created, remembered in the browser.
  • Engagement events: the team setting workflows_config.capture_workflows_engagement_events, which captures $workflows_email_* events.

Who sees what

  • The first run shows on the Workflows tab when the flag is on and the project has no workflows. It reuses the existing detection, workflowsSetupLogic (needs-setup), and takes the place of workflowsEmptyState content there. Organization age does not matter.
  • Once the project has a workflow, the tab shows the normal list.
  • Quick start keeps the rule every product's quick start uses today: organizations younger than three months (organizationLogic.isCurrentOrganizationNew), until every task is done or skipped. Under the flag the Workflows task list changes (below); the visibility rule does not.
  • With the flag off, nothing changes.

Templates declare what starts them (backend)

  • Each global template JSON with an email step gains a starts_on object: { kind: "event" | "no_event" | "schedule", events: string[], detail: string }.
    • events lists the event names that can drive the template, in order of preference. The welcome sequence lists common signup event names after its own trigger event.
    • detail is the short qualifier the card shows ("on /onboarding", "for 14 days", "every 10 days").
  • HogFlowTemplateSerializer returns it as a read-only, nullable field with help_text. Team and organization templates return null. hogli build:openapi regenerates the types.
  • A template's trigger that names an event must agree with starts_on.events[0].
  • Templates with an empty event trigger (filters: {}) declare the event their name implies. The first run writes the matched event into the trigger at creation, so the created workflow can go live.
  • Global templates must not carry sample person filters from their export. Clear any that remain.

Tailored gallery (frontend)

  • Source: the existing global templates list (api.hogFlowTemplates.getHogFlowTemplates, the same data as workflowTemplatesLogic), kept to gallery templates.
  • Seen events: one call to the generated eventDefinitionsList with the union of all starts_on.events names. A definition exists only once the event was seen.
  • Fit, per template:
    • event and no_event: ready when one of starts_on.events was seen. The first seen name is the matched event.
    • schedule: ready when the project has ingested any event (currentTeam.ingested_event).
    • Not ready: the card says "Your app does not send <event> yet".
  • Recommended starter: the welcome sequence when it is ready, otherwise the first ready template in list order. None ready means no recommendation. A later Jev decision (#240) may replace this rule; keep it in one selector.
  • Order: recommended first, then other ready templates, then the rest. "Start playing" is always last.
  • Filter: "Picked for your data (n)" by default, "All email templates (n)" beside it. When nothing is ready, the gallery shows all.
  • Cards reuse WorkflowTemplateCard with the cover image and WorkflowTemplateSteps. The footer shows only the starts-on line, with the event as the real event chip.
  • "Start playing" calls the existing newWorkflowLogic.createEmptyWorkflow, so the AI-first experiment still governs the blank path.
  • A link opens the full template chooser for anything else.

Make it yours (frontend)

  • Picking a gallery card opens Make it yours in the Workflows tab, addressed by a search param with the template id, so back and reload return to it. Edits live in the logic only.
  • Left: EmailTemplater with type="native_email" and layout="inline", on the template's first email step, with the first-run sender as from.
  • Right: "Change with PostHog AI", "Send me a test", the primary "Enable and open workflow", then the "Enable workflow" and "Capture engagement events" switches, both on.
    • With "Enable workflow" off the button reads "Open workflow".
    • The side column stacks under the editor in a narrow container (container query).
  • Send me a test sends the current editor content to the signed-in user's email from the first-run sender. It uses the test invocation endpoint the library test send uses (a throwaway flow with one email step, POST hog_flows/new/invocations). A "Skipping send:" result shows its reason, the way broadcastTestSendLogic.findTestSendSkipReason reads it. No modal.
  • Enable and open workflow exports the editor content, then creates the workflow in one POST hog_flows/ call:
    • every email step gets from.integrationId from the first-run sender;
    • the first email step gets the edited content;
    • the trigger event becomes the matched event when it differs;
    • status is active with the switch on, draft with it off.
    • Then it opens the workflow editor and remembers the id as the first-run workflow.
    • A rejected create shows the server's message on the screen and creates nothing.
  • No first-run sender (no own verified sender and no sandbox sender for the team): "Send me a test" and "Enable workflow" are disabled with a reason that links to Channels. "Open workflow" still creates a draft.

First-run sender (the seam)

  • One selector, firstRunSender, returns the sender for both the created workflow and the test send:
    1. the team's own verified email sender, if it has one;
    2. else the sandbox sender, ensured once through the frontend ensure call from #237;
    3. else none.
  • Nothing else in the first run names a sender. If #254 later picks a separate shared sender, only this selector changes.

Change with PostHog AI

  • Opens the PostHog AI side panel (openSidePanel(SidePanelTab.Max, prompt)) with a prompt about the email being edited.
  • Make it yours exposes the email (subject, preheader, text) as scene context, following workflowAgentContext.ts and templateAgentContext.ts.
  • PostHog AI applies a change to the open editor through a frontend-applied tool, following the create_message_template MaxTool (products/workflows/backend/max_tools.py with <MaxTool>). The editor reloads the new design.

Draft and sending banners (frontend)

  • Shown above the tabs of the first-run workflow while the flag is on. Dismissible, remembered per workflow in the browser.
  • Draft: "This workflow is not sending yet." with what the workflow does, and Enable highlighted through the existing setup highlight on [data-attr="workflow-launch"].
  • Sending: "Your workflow is sending." with "View metrics", which opens the Metrics tab.
  • While engagement events are off, the sending banner offers "Enable engagement events".
  • Who it reaches, the sender address and the own-domain line come from the slice blocked on #254.

Engagement events

  • The "Capture engagement events" switch and the banner action both turn on workflows_config.capture_workflows_engagement_events, sending only that field, as the email step's existing "Enable engagement events" does in HogFlowFunctionConfiguration.tsx.
  • Only admins can change workflows_config. Non-admins see the switch off and disabled with a reason, and the banner action hidden.
  • The Metrics tab works either way, because it reads the workflow's app metrics.
  • Sandbox sends carry no open or click tracking (#229). The setting still captures sent and delivered events for them. Copy must not promise opens and clicks from the sandbox sender.

Who the first sends reach (waits on #254)

  • The helper under "Enable workflow" and the banners name the sender address and who receives the emails.
  • The own-domain line: none, a link to set up the team's own domain; verifying, says so; verified, nothing, because the switch banner from #238 takes over.
  • Under members-only (options A or C on Shared sender or members-only: who may the first run's first sends reach? #254) the copy says the emails reach organization members until the own domain is verified, and the own-domain line becomes the main next step. Under shared sending (option B) it stays a quiet line.

Missing data handed to an agent

  • Above the gallery, at most one banner:
    • No seen signup event (the welcome template's starts_on.events): info, "You can test an email right now. To send it to real signups, your app needs to tell PostHog when someone signs up."
    • Else no email person property definition: warning, people have no email yet so they would miss the emails.
  • Each banner has "Copy prompt for your coding agent" (reuse AgentPromptButton) and "Let the Wizard do it", which reveals the command from useWizardCommand in WizardCommandBlock. Hidden when isCloudOrDev is false.
  • The check uses property definitions, not a person scan.

Quick start under the flag

  • The Workflows task list becomes: "Send yourself a test email" (new id), "Turn on your first workflow" (LaunchWorkflow, no dependsOn), "Send from your own domain" (new id).
  • New ids go into posthog/models/team/setup_tasks.py, then hogli build:openapi.
  • Send me a test completes the first. Enabling completes the second, as workflowLogic already does. A verified own sender completes the third.

Analytics

Captured from the frontend, each after the action succeeds:

  • workflows first run gallery shown: ready_count, recommended_template_id.
  • workflows first run template picked: template_id, recommended, ready.
  • workflows first run test sent: skipped.
  • workflows first run workflow created: template_id, enabled, engagement_events.
  • workflows first run agent help shown: missing (signup_event or email), and workflows first run agent prompt copied with the same property.

Flag

FEATURE_FLAGS.WORKFLOWS_FIRST_RUN = 'workflows-first-run', owner #team-workflows. The sandbox path also needs workflows-sandbox-sender on for the team; without it the first run falls back to an own sender or none.

Testing Decisions

A good test drives the feature through its public entry point and asserts what a user or the API sees: the API response, the request the frontend sends, the rendered screen. It does not assert private helpers. Each test must catch a regression no existing test catches (/writing-tests).

Seams (all exist today):

  1. Django API: the global templates list through HogFlowTemplateViewSet (APIBaseTest), plus a test over the template files: every gallery template declares a valid starts_on, and an event trigger agrees with it. Prior art: products/workflows/backend/tests/api/test_hog_flow_template.py.
  2. Kea logic tests with the API mocked at the HTTP layer (useMocks): the gallery logic (fit, recommendation, order, filter), the Make it yours logic (the create payload: sender on every email step, edited first email, trigger event, status per switch; the test invocation; skip reasons; the engagement update), the first-run sender selector, the banner logic, and the quick start completion. Prior art: workflowLogic tests, messageTemplateTestSendLogic, productSetupLogic.test.ts.
  3. Storybook stories for each screen and state, rendered and screenshotted at full width and at 520px. Prior art: the Workflows scene and template chooser stories.
  4. The PostHog AI tool through the backend tool test pattern of max_tools.py, with the model mocked.

Mock only what is external: the LLM and the PostHog analytics client. Fixture addresses use example.com.

Out of Scope

  • Brand detection. The Email brand work (#198) adds it later.
  • The sandbox sender itself (#225).
  • Entry points from other PostHog products (#218).
  • A recommendation made by Jev (#240).
  • The prototype's line of recent signups, its curved arrow, and its simulated inbox.
  • A database marker for the first-run workflow.
  • An empty state for a Metrics tab with no data yet.
  • Rollout of the flag.

Amendments (2026-10-05)

  • Sender reach: option C on #254. First sends reach verified organization members only. The first run shows, as numbers, how many people the workflow reaches and how many it does not reach yet, and "Send from your own domain" is the main call to action, opening the email domain wizard once it is on master. Lands in #264.

  • Every email of a template. Make it yours lists all of a template's emails in flow order with their timing, keeps edits across switching, and writes every edited email into the created workflow. "Send me a test" and PostHog AI act on the email that is open. Lands in #260 and #261.

  • PostHog AI open from the start. Make it yours opens the PostHog AI side panel with the open email selected as context, so a user can type changes straight away. Lands in #261.

  • Email brand as a later slice. Apply the saved Email brand on Make it yours, once the Email brand work is on master.

Further Notes

Activity

  1. Silthus commented on Oct 5, 2026

    @Silthus
    OwnerAuthor

    Closed on purpose as the reference document for the first-run implementation tickets, so it stays off the frontier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions