Skip to content

Email API: /workflows/api-sends redirect route #473

Closed

Description

@Silthus

Map: Map: send email from code with one PostHog call
Spec: Spec: email send API

Cast: Opus 5.5, high
Repository: PostHog/posthog (frontend)

Question

Can a person open one fixed link and land on their project's API sends?

Delivers end to end. A frontend route /project/{id}/workflows/api-sends, registered in the Workflows manifest. It lists flows with ?type=api_send and replaces the URL with that flow's Invocations tab. With no flow it shows "No emails sent through the API yet" and a link to the docs page; it never creates the flow. No other Workflows UI change. Spec section: "API sends redirect route".

Test seams. A kea logic test for the route's logic: with a flow it redirects to the Invocations URL; without one it reports the empty state and makes no write request.

Write scope. products/workflows/manifest.tsx, a small new scene and logic under products/workflows/frontend/, their test. Copy through /writing-user-facing-copy; components per /writing-ui-components.

Proof.

  • Red: the logic test for the redirect fails (no route).
  • Green: both cases pass.
  • Local: open the link on a dev team before and after creating the flow from a Django shell; screenshots of the empty state and the landed Invocations tab go in the PR.

Acceptance criteria

  • With a flow, the link lands on its Invocations tab and the back button does not loop.
  • Without a flow, the empty state shows and no flow is created.
  • The Workflows list is unchanged.

How to build (map #461)

  • /tdd red-green-refactor, outside-in, at the seams named above. Real Postgres, Valkey and ClickHouse; mock only what the seam section allows.
  • Repo skills apply where the diff touches their area: /writing-tests, /improving-drf-endpoints, /django-migrations, /adding-api-scopes, /writing-ui-components, /writing-kea-logics, /writing-user-facing-copy, /writing-code-comments.
  • Public repo: no usage numbers, customer names, internal metrics, Slack content or security reasoning in code, commits or the PR. Fixture addresses use example.com. The private note the map points at is background only; never quote it.
  • Shipped means a draft PR handed off for review (see the map's Notes for remotes and branches). Never mark ready, enqueue or merge.

Activity

  1. self-assigned this
    on Oct 14, 2026
  2. Silthus commented on Oct 14, 2026

    @Silthus
    OwnerAuthor

    Runtime: Claude Code background Agent
    Worker: implement-473-api-sends-redirect
    Cast: Opus 5.5, high. Wall-time cap: 120 minutes.
    Lands on integration branch spec/email-send-api, in parallel with the other open ticket on it (scopes do not overlap).

  3. Silthus commented on Oct 14, 2026

    @Silthus
    OwnerAuthor

    Claimed by conductor dispatch. Runtime: Claude Code background Agent, worker implement-473-api-sends-redirect, Opus 5.5 high. Integration branch: spec/email-send-api.

  4. Silthus commented on Oct 14, 2026

    @Silthus
    OwnerAuthor

    Landed on spec/email-send-api as a fast-forward from 38d8566764f.

    Landed commit: 64ab79c5a68 (feat(workflows): open api sends from a fixed link)

    What landed

    • products/workflows/manifest.tsx: the scene WorkflowsApiSends, the route /workflows/api-sends and urls.workflowsApiSends(). The route sits before /workflows/:tab, because the scene router takes the first match. Without that order the link opens as a Workflows tab. frontend/src/products.tsx and productScenes.tsx are regenerated.
    • products/workflows/frontend/ApiSends/apiSendsSceneLogic.ts: on mount it calls the generated hogFlowsList with type: 'api_send', limit: 1. With a flow, it calls router.actions.replace(urls.workflow(id, 'invocations')). The link leaves no history entry, so the back button does not loop. A resolution reducer holds loading | redirecting | empty | error. The logic only reads.
    • products/workflows/frontend/ApiSends/ApiSendsScene.tsx: a spinner while loading or redirecting. EmptyMessage shows "No emails sent through the API yet" with a "Read the docs" button to https://posthog.com/docs/workflows/email-api. An error banner has a "Try again" action.
    • The Workflows list and its tabs did not change.

    Proof

    Artifacts are on branch assets/email-api-473.

    • Red (base tree plus the new test): red.log. The suite fails because the logic does not exist and no route exists.
    • Red, one piece changed: red-push-instead-of-replace.log gets PUSH, not REPLACE, so a back-button loop fails the test. red-wrong-type-filter.log lands on an ordinary workflow and skips the empty state.
    • Green on the landed tree: green.log. Both cases pass.
    • Gate: the pre-push hogli ci:preflight --strict passed with 0 failures on the push to spec/email-send-api. The frontend tsgo --noEmit reported no errors (typecheck.log). oxlint and oxfmt are clean on the new files.
    • Rendered: the full App ran in Storybook with MSW mocks, from a scratch story that is not in the commit. All data is mock data.
      • Empty state, no API sends flow: empty-state.png
      • With a flow, the link lands on the Invocations tab of "API sends": redirect-invocations.png. The two error toasts come from endpoints the scratch story did not mock.

    Test seam

    apiSendsSceneLogic, mounted after a push to /workflows/api-sends, with the hog flows list mocked through MSW. The mock answers ?type=api_send with the API sends flow or nothing. It answers every other query with an ordinary workflow, so a missing filter fails the test.

    • With a flow: the pathname becomes /workflows/<id>/invocations and router.lastMethod is REPLACE.
    • Without a flow: resolution is empty, the URL stays, and the POST and PATCH hog flow handlers get no calls.

    Calls I made

    • Storybook, not the live app. The running dev stack on this box serves another checkout, so I could not load this branch's frontend into it. Storybook rendered the real scene and the real Workflow scene with mocked API responses. The ticket asked for a Django shell flow on a dev team. The mocked list response takes its place.
    • No feature flag on the route. The flow exists only after a send behind workflows-email-api. Without the flag a person sees the empty state.
    • Reused EmptyMessage, not ProductEmptyState. This is a single-link landing page, not a product's first-run gate.
    • No breadcrumbs. The page shows a spinner or the empty state, and then the Workflow scene sets its own.

    Follow-ups

    • The docs link targets /docs/workflows/email-api, which the docs ticket ships.
    • From Email API: Django creates and owns the hidden API sends workflow #469: the builder still shows Save draft and Publish for the API sends flow. Each save answers 400. This ticket left that alone because its scope says no other Workflows UI change.
    • On this box, the full typescript:check with default checkers reaches the heavy slice's memory limit and stalls. tsgo --noEmit --checkers 1 finishes in about 90 seconds.
    • No review ran in this ticket. Per the map, the ship ticket Ship the email send API as one reviewed draft PR #477 reviews the branch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions