Repository navigation
Conversation
posthog.emails.get(id) reads GET messaging/emails/{id}/ on the app host
with the secret key and returns { id, status, createdAt, statusAt,
reason? } or the error. It never retries: a new email answers not_found
for its first seconds, so the caller reads again later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s scope - emails.get with an id that has a lone surrogate returns invalid_message_id instead of throwing. - Without a secret key, emails.get names the hog_flow:read scope. - The EmailError code docs list invalid_message_id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coherence4 spec problems · 0 failing chokepoints · 2 spec gaps · 0 guard failures 44 components, 7 bullets (5 invariants, 2 requirements), 4 problems Spec problems (spec --check)
Chokepoints: none declared. Spec gaps: 2 entrances with outside or unknown trust and no traced control, and 10 more held by the adoption baseline
Close one with |
Node SDK compliance v2Commit: fc5dc17 Capture v0 — acceptance✅ 28 passed / ❌ 3 non-passing / Drill-down: run artifacts → ❌ server_snapshot_payload_false — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: ❌ server_snapshot_payload_zero — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: ❌ server_snapshot_payload_null — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: Harness exit: 1; report validation exit: 1. Capture v1 — acceptance✅ 28 passed / ❌ 3 non-passing / Drill-down: run artifacts → ❌ server_snapshot_payload_false — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: ❌ server_snapshot_payload_zero — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: ❌ server_snapshot_payload_null — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: Harness exit: 1; report validation exit: 1. Capture v0 — migration✅ 55 passed / ❌ 1 non-passing / Drill-down: run artifacts → ❌ migration/yaml-parity-v1/remote-flags-v1.feature::Omitted GeoIP settings default to enabled in flag requests — failed_assertionFlags field differs Code: Failed step: migration/yaml-parity-v1/remote-flags-v1.feature:134 the first flags request field "geoip_disable" should equal JSON false Operation: Field: Expected: false Actual value: true Harness exit: 1; report validation exit: 1. Capture v1 — migration✅ 120 passed / ❌ 1 non-passing / Drill-down: run artifacts → ❌ migration/yaml-parity-v1/remote-flags-v1.feature::Omitted GeoIP settings default to enabled in flag requests — failed_assertionFlags field differs Code: Failed step: migration/yaml-parity-v1/remote-flags-v1.feature:134 the first flags request field "geoip_disable" should equal JSON false Operation: Field: Expected: false Actual value: true Harness exit: 1; report validation exit: 1. |
Moved here from PostHog#5372, closed on 2026-10-25 so this fork stack is the only copy. It opens upstream again after review.
Problem
After a send, an app cannot read back what happened to the email, so it cannot show "sent" or "bounced" next to a user.
This PR adds
posthog.emails.get(id)from the shared SDK contract PostHog/posthog#120921, "Read":Slice 11 of strand Silthus/posthog#536, map Silthus/posthog#461.
Stacked on #5373 (code-form fields), on #5371 (slice 10), on #5312. This fork PR targets
main, so its diff includes all three. This layer alone:feat/node-emails-code-fields...feat/node-emails-get.Server dependency, not built yet:
GET /api/projects/@current/messaging/emails/{message_id}/is slice 08 of the send-api strand (the spec's pin). This PR is built against the contract at the HTTP seam.Impact
Pays into Grow Workflows through billed
workflows_emailsfrom API sends (not measured yet: the send attribution of PostHog/posthog#120628 plus its insight). In scope: Messaging, email. Expected move: small, up: the status read lets a Resend site that shows delivery state move. Run cost: n/a.Merge Danger
Caution
🔴 Merge risk: high · one-way door (merge-risk rubric)
posthog.emails.get()to the public API. Once released, the status names and fields are a contract callers branch on.Changes
emails.get(id)readsGET messaging/emails/{id}/on the app host with the secret key, the id as one encoded path segment.{ data: { id, status, createdAt, statusAt, reason? }, error: null }or{ data: null, error }, with the same error names assend.reasonis left out when PostHog gives none.404,429,503or a network error. A new email answers404 not_foundfor its first seconds, and the JSDoc says so.statusis not Resend'slast_event.missing_secret_key(naming thehog_flow:readscope),missing_app_host,client_disabled,client_closedaftershutdown(), assenddoes, andinvalid_message_idfor an empty id or one that can't be encoded into a URL, namedvalidation_erroras the contract's local checks are.Parity with posthog-python
id,status,created_at,status_at,reason?id,status,createdAt,statusAt,reason?statusvaluesqueued,waiting,unknown,skipped,failed,sent,delivered,bounced,complainedEvidence
emailsnamespace on a real client,fetchmocked byFakeEmailsApi, which now answers the status route (emails.spec.ts,describe('get')).Authorization,User-AgentandX-PostHog-Migrated-From; the field mapping with and withoutreason; one request and the error for404,503,429, a network failure and an answer without a status; the refusals before any request, with the scope in the missing-key message and a lone-surrogate id; an id with/and?read as one path segment.get, then green. The review fix's 2 cases failed before it, then green.fc5dc171(on the leased compute box): nodevitest(1350 passed), the edge build tests (1350 passed),tscfor the build and the type tests,oxlint,oxfmt, build andgenerate-references; corevitest.getuntilsent): gap. The read route is slice 08, not built yet.Release info Sub-libraries affected
Libraries affected
Checklist
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Michael (@Silthus) decided the read path in the spec. Built by Claude Code (Claude Opus 5.5) test-first, as one build lane of the map.
GET messaging/emails/{message_id}/,hog_flow:readstatusvalue from a newer serverEmailStatusgetaftershutdown()client_closed, asgetalready refuses withclient_disabled; the contract names only a sendOpen: none.
Involvement: spec decisions by Michael; one strand review by GPT-6.1 Sol over PostHog#5312, PostHog#5371 and PostHog#5372 (2 findings: 1 fixed in PostHog#5371, 1 rejected as behavior that predates this strand) and a final read of the fix with no findings; a second final read after the restack on PostHog#5373 and the
invalid_message_idname fix, with no findings; a QA swarm over the restacked stack in two rounds (Review lane GPT-6.1 Sol with seven lenses, Fresh-eyes lane Claude Opus 5.5) with its fix in this PR; no live run yet.🤖 Generated with Claude Code