Repository navigation
Conversation
Document how to make a Workflows email template transactional and send it with POST /api/projects/:project_id/messaging_templates/:template_id/send/. The page covers the API key, the request and response, idempotency keys, error codes, rate limits, and how transactional emails behave. The page is marked beta. Co-Authored-By: Claude Opus 5.5 <[email protected]> Generated-By: PostHog Desktop Task-Id: a0274fa0-d9be-48bf-b601-f153decf241d
…e SDKs - Send through `POST /messaging/send/email/` with `template.id`. The template route still works. - Read an email's status with `GET /messaging/emails/:message_id/`: statuses, reasons, 404 cases, scopes and read limits. - Send and read with the Python and Node.js SDKs. - Add `recipient_unreachable` to the errors table. Co-Authored-By: Claude Opus 5.5 <[email protected]> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
Co-Authored-By: Claude Opus 5.5 <[email protected]> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
- The Node.js example turns off local evaluation, and both examples keep the SDK's error. - The status examples handle the error first and explain retrying a not_found. - The SDK section adds install, client reuse, timeouts and retries, uncertain outcomes, and a no-network testing example. Co-Authored-By: Claude Opus 5.5 <[email protected]> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
…m pages The overview keeps the template, key, idempotency, errors, statuses and behavior. Node.js, Python and API pages hold the setup, send, status, retries and testing for each platform, with a sidebar group like push notifications. Co-Authored-By: Claude Opus 5.5 <[email protected]> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
…he overview - A quick start links to the Node.js, Python and API pages. - Creating an email follows the Transactional section's wizard, and the hidden workflow explanation is gone. - Agents can set it up with the PostHog MCP server. - Protect sensitive data turns off click tracking and keeps deliveries, opens and statuses. Co-Authored-By: Claude Opus 5.5 <[email protected]> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
Co-Authored-By: Claude Opus 5.5 <[email protected]> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
The send endpoint now takes a flat body (PostHog/posthog#126287), so the field table, the curl example and the engagement events note use template_id and distinct_id. Co-Authored-By: Claude Opus 5.5 <[email protected]> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
…il API page Document the code form of the send route (template_key with the subject and body), capture_engagement, the checks that only apply to it, and a delivery_unavailable anchor for the 503's docs link. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…mits Add DKIM-Signature and Received to the reserved headers, the header name and value limits, the tag length, and that reply_to takes bare addresses. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…only Co-Authored-By: Claude Opus 5.5 <[email protected]>
…t template_key and ph_ tags do - A retry with the same Idempotency-Key is the same request when every field of the code-form table matches. - template_key groups the email's metrics and sends. - A tag name that starts with ph_, in any case, is refused. - capture_engagement on a template send defaults to the project's setting. - Drop a double blank line after the checks table (fix-mdx). Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ell out the ph_ case rule - The template-send table gets a capture_engagement row with the project default, and says a retry sends the field the same way. - The code-form row keeps only its own default. - A ph_ tag name is refused in uppercase or lowercase. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Deploy preview
Changed pages
|
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 673346e: 1 Should fix.
This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.
See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.
| | `to` | Yes | The email address of the recipient. One address for each request. | | ||
| | `message_data` | No | An object with the values for the template's `{{ variables.* }}` placeholders. For example, `{"reset_link": "https://example.com/reset"}` fills `{{ variables.reset_link }}`. The template can't read person properties, so pass all the data that the email needs here. | | ||
| | `distinct_id` | No | The distinct ID of the recipient's person. PostHog records the send and its [engagement events](/docs/workflows/engagement-events) against this person. If you leave it out, PostHog uses the email address as the distinct ID, which might not match the person in your product. We recommend that you always send it. | | ||
| | `capture_engagement` | No | Whether PostHog captures the email's [engagement events](/docs/workflows/engagement-events). The default is the project's **Capture email engagement events** setting. Send `false` to turn capture off for this email. On a retry with the same `Idempotency-Key`, send this field as you did the first time, or PostHog returns `idempotency_key_reused`. | |
There was a problem hiding this comment.
P2 · Update the shared retry contract for capture_engagement
For template sends retried with changed capture_engagement, this row specifies idempotency_key_reused, but the linked overview in transactional-emails/index.mdx:101 defines identical requests using only template, to, distinct_id, and message_data. Readers following that retry contract encounter an unexpected 422; update the overview's definition in this change.
Bundle reportTotal JS (gzip)11.71 MiB (+0.1 KiB / +0.0%) Eager graph (modules shipped in each entrypoint's initial chunks)
Largest modules in the
|
| Module | Size |
|---|---|
css ./node_modules/.pnpm/[email protected][email protected]/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[1]!./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[2]!./src/styles/global.css |
839.5 KiB |
./src/components/Stickers/Stickers.tsx |
696.4 KiB |
./node_modules/.pnpm/@[email protected][email protected][email protected][email protected]/node_modules/@posthog/icons/dist/posthog-icons.es.js |
354.8 KiB |
./node_modules/.pnpm/[email protected]_@[email protected][email protected]/node_modules/react-markdown/lib/react-markdown.js + 88 modules |
351.4 KiB |
./node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/framer-motion/dist/es/render/components/motion/proxy.mjs + 126 modules |
287.2 KiB |
./node_modules/.pnpm/[email protected][email protected]/node_modules/cloudinary-core/cloudinary-core.js |
281.9 KiB |
./src/components/SearchUI/index.tsx + 87 modules |
273.1 KiB |
./gatsby-browser.tsx + 59 modules |
259.1 KiB |
./node_modules/.pnpm/@[email protected][email protected]/node_modules/@posthog/brand/dist/generated/hoggies/svg/construction-1.mjs |
248.5 KiB |
./src/components/Pricing/PricingSlider/Slider.tsx + 87 modules |
240.2 KiB |
./node_modules/.pnpm/@[email protected][email protected]/node_modules/@posthog/brand/dist/generated/hoggies/svg/construction-2.mjs |
228.2 KiB |
./node_modules/.pnpm/@[email protected][email protected]/node_modules/@posthog/brand/dist/generated/hoggies/svg/magnifying-glass.mjs |
221.7 KiB |
./src/hooks/skills.tsx + 2 modules |
150.7 KiB |
./src/hooks/useProduct.ts + 9 modules |
149.9 KiB |
./src/components/Icons/Icons.tsx |
147.4 KiB |
Eager-graph budgets are report-only until a baseline is established. Sizes are gzip of public/**/*.js; eager size is webpack module source bytes for the modules actually shipped in the entrypoint's initial chunks (post-tree-shake).
Co-Authored-By: Claude Opus 5.5 <[email protected]> # Conflicts: # contents/docs/workflows/transactional-emails/api.mdx # contents/docs/workflows/transactional-emails/node.mdx # contents/docs/workflows/transactional-emails/python.mdx
…code send The first release sends content from code with template_key, subject, html and text only. React Email output is rendered by the app and sent as html. Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Drop capture_engagement from the template send: the send route refuses it there, with the other fields of content from your code. - Fold sender_not_allowed into sender_not_verified, the code the first release returns for a from address no verified sender can use. - Add "Send content from your code" to the Node.js and Python pages, with a React Email example rendered by the app. Co-Authored-By: Claude Opus 5.5 <[email protected]>
… in a replay - A field error is a 400 with type validation_error, a field code such as required or invalid, and attr naming the field. - A replay also compares the sender PostHog chose, so a retry without from after the default sender changed returns idempotency_key_reused. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ntify distinct ID Add "Link emails to your users" to the API, Node.js and Python pages: pass the distinct ID you pass to identify(), what happens without one today (a separate person keyed by the email address), and that invites and signup checks can leave it out. The overview and the API tables point there instead of "always send it". Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Node.js and Python client examples used <ph_project_api_key> and a hard-coded ingestion host, which the site doesn't replace. They now use <ph_project_token> and <ph_client_api_host>, as the SDK pages do, so a logged-in reader sees their own project token and host. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Changes
An engineer who renders their own emails, such as React Email components, finds no way in the transactional email docs to send that content through PostHog. The pages cover only templates made in PostHog.
template_key,subject,html,text,from,reply_toandcapture_engagement, a cURL example, and the checks of a code send.@react-email/renderand sends the result ashtml.identify(), so the send and its sent, opened, and clicked events land on that user's person. Without it, PostHog records the email against a person whose distinct ID is the recipient's email address, as master does today (distinct_id or to). Someone who isn't a user yet, such as an invite, needs no distinct ID. The overview page and the API page'sdistinct_idrows say the same and link to the section.delivery_unavailablegets its own section on the API page. A link to#delivery_unavailableopens it.This revision (2026-11-03) adds "Link emails to your users" (
5cffbd0,51f427b, and the style fixes inc42c7c3). It describes only what master does whendistinct_idis missing.The revision before (2026-11-02) matches the pages to the first release:
capture_engagementleaves the template-send table. The first release refuses it on atemplate_idsend, with every other field of content from your code.sender_not_allowedfolds intosender_not_verified, the code the first release returns for afromthat no verified sender can use.sender_not_allowedcomes later, with #127181.400withtype: validation_error, a field code such asrequired, andattr. The page saidinvalid_request, which comes later too.fromafter the default sender changed returnsidempotency_key_reused.Note
🔵 Merge risk: low · two-way door
Warning
The SDK pages name posthog-node 5.62.0 and posthog 7.83.0, the next minor release of each. Confirm both numbers when the SDKs release, before this merges.
Checklist
vercel.json(no page moved)Evidence
933d907f3, with@react-email/render1.1.2. It sends onePOSTto/api/projects/@current/messaging/send/email/with the flat body, andtscpasses. The Python sync and async samples ran against posthog-python at Update events.md #1171's20fad8c, with the same body. Raw output below.SendEmailSerializeron #127123 at51409d9b86b, and the replay fingerprint on #127122 atb5889c9eeca.2610e8d(api, node, python): the API page renders the#delivery_unavailableand#send-content-from-your-codeanchors,sender_not_verified,validation_errorand the template-send sentence, and nosender_not_allowed. The Node.js and Python pages render their own#send-content-from-your-codesection with the link to the API page.c42c7c3(index, api, node, python): the API, Node.js and Python pages render#link-emails-to-your-userswith theidentify()sentence, the email-address bullet and the not-a-user-yet bullet. The API page's twodistinct_idrows link to it, and the overview renders the new distinct ID item.c42c7c3.scripts/fix-mdx.jsleaves them unchanged. CI runs Vale, markdownlint and spelling too.Sample runs (raw)
The pages' samples are the same on
7295a66a7eand this head;2610e8dchanged only API page text.Decisions
@react-email/renderand sendshtml; the SDK adds no React optioncapture_engagementon a template send🤖 Agent context
Agent: Claude Code, Claude Opus 5.5 (
claude-opus-5-5), as the docs, skills and wizard slice of Silthus/posthog#461. Skills:full-stop,writing-user-facing-copy,writing-pr-descriptions. Reviews infleet proof: GPT-6.1 Sol (high), round 1 on7295a66a7e(2 findings, both fixed in2610e8d1d1) and round 2 on the fixes (no findings). Round 3 on5cffbd0(the new section) and final reads on51f427bandc42c7c3: no findings.Earlier revisions of this description
The two "Impact" paragraphs of the earlier text are removed: they named internal metrics. The rest is unchanged.
Changes
Moved from Silthus#1.
This PR adds the code form of the transactional email send to the API page that PostHog/posthog.com#21564 adds. With the code form, your code sends the subject and the body, and PostHog needs no template. The PR also adds the checks that apply only to the code form, and a
delivery_unavailablesection. Thedocs_urlof the send route's503opens that section.The PR stacks on
posthog/docs-transactional-email-api, the branch of #21564. It changes one page:contents/docs/workflows/transactional-emails/api.mdx, +59/-1. Before, this PR rewrote our own copy of the page. #21564 now owns that page, so this PR keeps only the part that #21564 does not have. Silthus/posthog.com#2 duplicated #21564 and is closed.Slice 13 of Silthus/posthog#461.
delivery_unavailable. A new section under "Send an email". It tells the reader that PostHog maybe queued the email, and to retry with the sameIdempotency-Key. The SDKs retry this on their own. The old sentence about the503moved into this section.template_key,to,distinct_id,subject,html,text,from,reply_to,cc,bcc,headers,tags,attachments,capture_engagement), the rules for headers, tags, attachments, and suppressed addresses, and a cURL example.capture_engagement. The code form and a template send both take it, so both tables list it. For content from your code, the default istrue. For a template send, the default is the project's Capture email engagement events setting, and a retry with the sameIdempotency-Keymust send the field as the first request did. Like the code form, this documents the send route of the server stack, notmaster.invalid_request,sender_not_verified, andsender_not_allowed. The errors of every send stay on the overview page.831841b73,b42086e4c,acd4a4b88):reply_totakes addresses without a display name. The reserved headers includeDKIM-SignatureandReceived. A header name has 1 to 126 visible ASCII characters, with no spaces and no colon; a value has at most 870 printable ASCII characters and can have spaces. A tag name or value has at most 256 characters.2502eb65aand673346e75, review fixes of rounds 5 and 6): a code-form retry with the sameIdempotency-Keyis the same request when every field of the table has the same value.template_keygroups the email's metrics and sends. PostHog refuses a tag name that starts withph_in uppercase or lowercase. The template-send table gets acapture_engagementrow. A double blank line after the checks table is gone (scripts/fix-mdx.js).Checklist
vercel.json(no page moved)Evidence
#send-an-email,#checks-for-content-from-your-code,/docs/workflows/transactional-emails#errors,/docs/workflows/engagement-events,/docs/workflows/opt-outs#the-suppression-list, and/docs/workflows/configure-channels. I checked each one in the source.masterand the code-form branch of the stack. The same-request rule follows the send route's idempotency check. Theph_rule follows the tag name check of the route and of the CDP schema, which both ignore case. Thecapture_engagementdefaults follow the route's serializer. The header name rule was checked against the route's header check for every ASCII code point, the empty name, and 126- and 127-character names.673346e75, 2026-11-02): feat(messaging): take template_id and distinct_id at the top of the send body posthog#126287 puttemplate_idanddistinct_idat the top of the send body onmaster. Both cURL examples on the page already send that shape, so this head needs no change. The base stays on docs(workflows): add the transactional email API page #21564's branch.673346e75:prettier --checkpasses (.prettierignoreskips.mdx).scripts/fix-mdx.js, theformat:docsstep, leaves the page unchanged (it removed one double blank line in2502eb65a). Vale 3.13.1 with the repo's.vale.ini: 0 alerts on the page.pnpm format's glob does not include.mdx.673346e75. Headless Chromium opened#delivery_unavailable,#send-content-from-your-codeand#checks-for-content-from-your-code: each heading renders and is visible, and the page text hascapture_engagement,Idempotency-Key,sender_not_verified,sender_not_allowedand theph_rule.Spelling, which fails before it runs: the base branch of docs(workflows): add the transactional email API page #21564 still sets up Python 3.8, which the runner image no longer has.masterfixed that in Bump spelling check to Python 3.12 #21771, so the job passes once docs(workflows): add the transactional email API page #21564 takesmaster. The same codespell fork, run locally on the changed page with.codespellignore, reports nothing.Decisions
capture_engagementon a template send?capture_engagement, its checks, and thedelivery_unavailableanchor. Close Silthus/posthog.com#2.Involvement: no grilling rounds, no prototypes. GPT-6.1 Sol (high) reviewed this revision in round 1 (one finding on the reserved headers) and in three reads of the fixes: the first two found one gap each in the header name rule, both fixed, and the third was clean. QA swarm 2026-11-01: panel round 5 (fresh-eyes, correctness, risk, design, anti-overengineering, sweep, michael): 5 findings, 4 fixed, 1 rejected (the dev-server load, a recorded gap); fixes round 6 (fresh-eyes): 2 findings, both fixed; final read 1 (fresh-eyes) on
673346e75: one nit on this description, fixed here. 7 fixed, 1 rejected. Proof: source checks and Vale; the rendered page checked in this PR's preview build on 2026-11-02.Follow-up:
capture_engagementon a template send is not live: PostHog/posthogmasterdoes not take the field yet. It comes with the code-form send route of the server stack, which this PR waits for (see the merge-risk callout). When that route ships, the overview's same-request list (index.mdx, the base page) becomes wrong for a template send that setscapture_engagement, so its fix must land with this PR or before it.🤖 Agent context
Agent: Claude Code, Claude Opus 5.5 (
claude-opus-5-5). Reviews: GPT-6.1 Sol (high) and Claude Opus 5.5 (high) in fleet proof panels on the fork. The reviewed head673346e75is pushed here unchanged.History: the earlier revision on our own page (before the rebase onto #21564)
No upstream PR: this page change lives only in the fork. It stacks on Silthus/posthog.com#2, which moved here from PostHog/posthog.com#21306 (closed on 2026-10-25).
Changes
Engineers who move transactional email off Resend need one page that shows how to send from code with the PostHog client they already use. The page taught the earlier
posthog-nodedraft (templateFile(),uploadTemplate(), "sends are not idempotent"). It now teaches the final send contract of PostHog/posthog#120921, in Node.js and Python, and every error code that the server and the SDKs emit has an anchor.Slice 13 of Silthus/posthog#461, strand Silthus/posthog#538.
New in
aed49fa8cto641b3bd85: the final contractThe 2026-10-22 and 2026-10-23 entries of the contract, against the server and SDK branches of the PRs below.
ccandbccaddresses). None of them work withtemplate_id.400 invalid_request, with the server'sattr,detail,why, andfix. The HTTP section and theinvalid_requestsection quote it.transactional_email_paused: quotes the new server text. A paused workflow is checked after the idempotency key lookup, so a retry of a queued send still gets its202. When a stored template's workflow is deleted, the send refuses before the key lookup. Rendered content whose key names no stored template gets a new workflow instead, so a retry answers422 idempotency_key_reused.idempotency_key_reusedis the422code on both send routes.invalid_idempotent_requestis its SDKname. One set of keys covers both routes.textfromhtml. When the HTML has no text, such as an email that is only an image, the email goes out without a plain-text part.from_.sender_not_allowed(the address isn't on a verified domain) andsender_not_verified(nofrom, and no verified default sender) quote the server text.GET messaging/emails/{id}/andposthog.emails.get(id)in Node.js, the statuses in order, the optionalreason, 30 days of retention, and the404for a new email. posthog-python has no read yet.GET messaging/emails/?to=<address>, short. Newest first and paged, as the facade of PostHog/posthog#121898 returns them. The API route and its contract amendment are pending (see Open).invalid_form,invalid_template_id,invalid_attachment, andinvalid_message_idget anchors, with posthog-node's messages.network_errorgets thenameservice_unavailable.hog_flow:writealso reads an email's status. A project secret API key uploads templates, sends them, and reads an email's status.What the page covers
template_idwithtemplate_data, and the code form bytemplate_key: rendered content, and local templates that the SDK uploads when they change.secretKey/secret_key,appHost/app_hostfor any host that isn't PostHog Cloud (and why the secret key never goes to an ingestion proxy),maxRetries/max_retries.MultiLanguagetabs), rendered content, React Email rendered tohtml, local templates and their sync loop, rolling deploys, the sender, and copies, headers, tags, and attachments.distinct_id, and why. Every send example passes it.202is queued, not delivered. The checks that would skip a send run first.409, the422, what the SDKs retry,max_retries=0with a key from the email's purpose for job runners, and the promise: one send per key on retries, not exactly-once delivery.{ data, error }in Node.js,EmailErrorin Python, the status tonametable with Resend's names), read an email's status, and send over HTTP (cURL with the wire fields and theIdempotency-Keyheader, and a parameter-to-request table).templateFile()anduploadTemplate()sections, posthog-node's source detection,template_file_unreadable,template_missing_subject, and "Sends are not idempotent".Not merged yet
The page documents these as decided. Hold this PR until they ship:
template_edited_in_posthog,sender_not_verified, derivedtext.202(sender_not_allowed,recipient_suppressed,sending_suspended,template_paused).emails.get).Evidence
id, and its section quotes the server's or the SDK's strings;nameof the contract is on the page;templateFileor "so every email has a text version" (quoted server text in code blocks is exempt);#link resolves.origin/feat/email-status-read(2cb033ad613) andorigin/feat/email-send-attachments, posthog-node atfeat/node-emails-get, and posthog-python atfeat/emails-code-form.9e73da0bf(the page before this revision), checked on the page source: 20 failures. The newinvalid_request,sender_not_verified,not_found, andtransactional_email_pausedtexts were missing, the four local check codes had no heading, and the page still said "so every email has a text version".931fdb8f1, served withgatsby serve:48/48codes anchored with matching text, 14 send examples all pass a distinct ID, 142 in-page links resolve.641b3bd85changes three sentences of prose only.pnpm build:minimalpasses: 3,982 pages, exit 0.-q 8 -I .codespellignore): no change. markdownlint passed in the pre-commit hook. No em or en dashes.Screenshots
Production build (
pnpm build:minimal,gatsby serve) at931fdb8f1, light, 1440 px. Example data only:example.comaddresses and invented IDs.invalid_request: unknown fields, copies, headers, tags, attachmentstransactional_email_paused(before641b3bd85scoped the deleted-workflow sentence)docs_urlforinvalid_attachment, opened by plain navigationAnchor check output (green, production build of 931fdb8)
Anchor check output (red, source of 9e73da0)
Earlier evidence, before the final contract (ce1b5ea to 6c55cf4)
Local dev server (
GATSBY_MINIMAL=true pnpm start) atce1b5ea56, before the review fixes, light, 1440 px. Example data only:example.comaddresses and invented IDs.distinct_id, what a successful send meanssender_not_verifiedtosending_suspendedtemplate_pausedtoidempotency_key_reuseddocs_urlfortemplate_edited_in_posthog, opened by plain navigationAnchor check output (green, b7c65ae)
Anchor check output (red, 2b0a7a2)
Open contract questions
Each is a place where the page, the contract, and the code don't agree yet. The page follows the contract unless the line says otherwise.
app_host_required, notmissing_app_host, the client's retry option isemail_request_max_retries,sendtakes no per-sendmax_retries(so the job runner example doesn't run there yet), and the result field isid, where the contract saysmessage_id. The page documents the contract names, usesresult.id, and anchorsapp_host_requiredtoo.template_repository_missing. Confirm, or the code form keeps source detection.template_repository_missingfix says "posthog-node detects it from the git checkout or the CI environment", and the change prompt saysposthog.emails.templateFile(). Both need a server change in the change request stack.replayedin Node.js. The server returnsreplayed, and posthog-python exposes it. posthog-node'sdatais{ id, warnings }, as in the contract. The page mentionsreplayedfor Python and HTTP only.<title>. Sending it as a local template needs the app to passsubjectandhtmlseparately, and whetherhtmlkeeps the<title>changes the hash. The page says to pass the subject assubject. The byte-for-byte round trip isn't proven.Closed since the last revision
422code:idempotency_key_reusedon both send routes, with the SDKnameinvalid_idempotent_request.fromin Python:from_.sender_not_allowed: the send checks define it, with server text. The page quotes it.transactional_email_paused(the workflow) andtemplate_paused(the send policy) both stay, with separate meanings. So dosender_unavailable(the upload) andsender_not_verified(the send).nameofnetwork_error:service_unavailable.app_host: the client takes it now. The rest of that question stays open as question 1.Decisions
docs_urlthe server builds today and after the contract lands opens a section.from_, as the contract now names it. Replacesfromwith no Python example.Open: the 6 contract questions above.
Involvement: 0 grilling rounds, 0 prototypes, 2 GPT-6.1 Sol review rounds (3 findings in the first, 2 in the second, all fixed), 6 final reads of the fixes (reads 1, 2, 4, and 5 found 1 finding each, all fixed; read 3 ran after Michael overrode the arbiter and was clean; read 6 was clean), proof captured (anchor check red and green, production build, Vale, codespell, markdownlint, screenshots).
Checklist
vercel.jsonWritten by Claude Opus 5.5 (
claude-opus-5-5) as the docs-api build lane of the map. Reviewed by GPT-6.1 Sol (high).🤖 Generated with Claude Code
🤖 Generated with Claude Code