diff --git a/contents/docs/workflows/transactional-emails/api.mdx b/contents/docs/workflows/transactional-emails/api.mdx index f65f5063fb61..81120497e52b 100644 --- a/contents/docs/workflows/transactional-emails/api.mdx +++ b/contents/docs/workflows/transactional-emails/api.mdx @@ -14,8 +14,8 @@ You need a [transactional email](/docs/workflows/transactional-emails#create-a-t POST https://us.posthog.com/api/projects/@current/messaging/send/email/ ``` -- **Host** - Use `https://us.posthog.com` or `https://eu.posthog.com`, the host where you log in to PostHog. Don't use an ingestion host like `us.i.posthog.com`. If you self-host PostHog, use the URL of your instance. -- **Project** - `@current` is the project that the key belongs to. You can use your project ID instead. +- **Host** – Use `https://us.posthog.com` or `https://eu.posthog.com`, the host where you log in to PostHog. Don't use an ingestion host like `us.i.posthog.com`. If you self-host PostHog, use the URL of your instance. +- **Project** – `@current` is the project that the key belongs to. You can use your project ID instead. ### Request @@ -178,8 +178,8 @@ A `503` with the code `delivery_unavailable` means that PostHog couldn't confirm Pass the distinct ID that you pass to `identify()` as `distinct_id`. This is usually your user's ID. PostHog then records the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on that user's person, in the same timeline as their product events. -- **No distinct ID** - PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. -- **Not a user yet** - For an invite, or an email address check before the account exists, leave `distinct_id` out. +- **No distinct ID** – PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. +- **Not a user yet** – For an invite, or an email address check before the account exists, leave `distinct_id` out. ## Send content from your code @@ -327,14 +327,14 @@ $result = PostHog::emails()->send([ Mark the email as sensitive when it carries login links, reset tokens, or codes. For a sensitive email, PostHog: -- **Keeps links unchanged** - PostHog doesn't rewrite the email's links to count clicks, so a token in a link never goes through a tracking redirect. [`tracking`](#open-and-click-tracking) can still turn clicks on. -- **Leaves the subject off engagement events** - The email's [engagement events](/docs/workflows/engagement-events) don't carry its subject. -- **Keeps no copy of the content** - PostHog doesn't keep the email's subject or body. +- **Keeps links unchanged** – PostHog doesn't rewrite the email's links to count clicks, so a token in a link never goes through a tracking redirect. [`tracking`](#open-and-click-tracking) can still turn clicks on. +- **Leaves the subject off engagement events** – The email's [engagement events](/docs/workflows/engagement-events) don't carry its subject. +- **Keeps no copy of the content** – PostHog doesn't keep the email's subject or body. `sensitive` takes `true` or `false`. If you leave it out: -- **A template has the same key** - When a template in your project has the key that you send as `template_key`, the email takes that template's [**Protect sensitive data**](/docs/workflows/transactional-emails#protect-sensitive-data) setting. -- **No template has the key** - The email is sensitive. +- **A template has the same key** – When a template in your project has the key that you send as `template_key`, the email takes that template's [**Protect sensitive data**](/docs/workflows/transactional-emails#protect-sensitive-data) setting. +- **No template has the key** – The email is sensitive. Send `"sensitive": false` for an email that carries no secrets, such as an order confirmation. PostHog then tracks its clicks too, as it does for a template without **Protect sensitive data**. `sensitive` doesn't change opens or engagement events. @@ -359,9 +359,9 @@ Send `"sensitive": false` for an email that carries no secrets, such as an order | `false` | Off | Off | Off | | An object | `opens`, on by default | `clicks`, default follows `sensitive` | `engagement`, on by default | -- **Opens** - PostHog adds an open pixel to the email, so it can count opens. -- **Clicks** - PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. -- **Engagement events** - PostHog captures the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever you send. +- **Opens** – PostHog adds an open pixel to the email, so it can count opens. +- **Clicks** – PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. +- **Engagement events** – PostHog captures the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever you send. A key that you leave out of the object keeps its default, so `{}` is the same as leaving out `tracking`. @@ -369,9 +369,9 @@ A key that you leave out of the object keeps its default, so `{}` is the same as When you leave out `clicks`, PostHog tracks clicks only for an email that isn't [sensitive](#sensitive-emails). A tracked link goes through a redirect, so a login link or a reset token in it would go through the redirect too. -- **An email without secrets** - Send `"sensitive": false`, and PostHog tracks its clicks. -- **A sensitive email with safe links** - Send `"tracking": {"clicks": true}`, or `"tracking": true`. PostHog tracks its clicks, and keeps the email's other protections. -- **An email that isn't sensitive** - Send `"tracking": {"clicks": false}` to keep its links unchanged. +- **An email without secrets** – Send `"sensitive": false`, and PostHog tracks its clicks. +- **A sensitive email with safe links** – Send `"tracking": {"clicks": true}`, or `"tracking": true`. PostHog tracks its clicks, and keeps the email's other protections. +- **An email that isn't sensitive** – Send `"tracking": {"clicks": false}` to keep its links unchanged. A `clicks` value that you send wins over `sensitive`. To keep one link as it is, add `data-ph-no-track` or `clicktracking="off"` to its `` tag, as for [workflow emails](/docs/workflows/library#disabling-link-click-tracking). @@ -395,10 +395,10 @@ In a form-encoded request, `tracking` and `sensitive` take `true` or `false`. Se PostHog returns a `400` and sends nothing when: -- **An unknown key** - The object has a key other than `opens`, `clicks`, and `engagement`. -- **Clicks without opens** - `opens` is `false` and `clicks` is `true`. Click tracking needs open tracking. Turn on `opens`, or turn off `clicks`. With `opens` set to `false` and `clicks` left out, PostHog tracks no clicks, whatever `sensitive` says. -- **Both fields** - The request has `tracking` and `capture_engagement`. Move the value of `capture_engagement` to `tracking.engagement`. -- **A template send** - The request has `template_id` and `tracking` or `sensitive`. A template uses its own settings. +- **An unknown key** – The object has a key other than `opens`, `clicks`, and `engagement`. +- **Clicks without opens** – `opens` is `false` and `clicks` is `true`. Click tracking needs open tracking. Turn on `opens`, or turn off `clicks`. With `opens` set to `false` and `clicks` left out, PostHog tracks no clicks, whatever `sensitive` says. +- **Both fields** – The request has `tracking` and `capture_engagement`. Move the value of `capture_engagement` to `tracking.engagement`. +- **A template send** – The request has `template_id` and `tracking` or `sensitive`. A template uses its own settings. ### Copies and attachments diff --git a/contents/docs/workflows/transactional-emails/dotnet.mdx b/contents/docs/workflows/transactional-emails/dotnet.mdx index 91e7f75a05c7..159365bcd76c 100644 --- a/contents/docs/workflows/transactional-emails/dotnet.mdx +++ b/contents/docs/workflows/transactional-emails/dotnet.mdx @@ -9,7 +9,7 @@ The [.NET SDK](/docs/libraries/dotnet) sends transactional emails from version 2
Prompt for your coding agent -Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that are easy to get wrong. +Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that agents often get wrong. ```text Add PostHog transactional email to this .NET app with the PostHog NuGet package, or PostHog.AspNetCore for ASP.NET Core. @@ -181,8 +181,8 @@ BackgroundJob.Enqueue(job => Pass the distinct ID that you pass to `IdentifyAsync` as `DistinctId`. This is usually your user's ID. PostHog then records the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on that user's person, in the same timeline as their product events. -- **No distinct ID** - PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. -- **Not a user yet** - For an invite, or an email address check before the account exists, leave `DistinctId` out. +- **No distinct ID** – PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. +- **Not a user yet** – For an invite, or an email address check before the account exists, leave `DistinctId` out. ## Send content from your code @@ -205,12 +205,12 @@ public async Task SendPasswordResetAsync(User user, string html, string } ``` -- **`TemplateKey`** - Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. -- **`Text`** - If you leave it out, PostHog makes it from `Html`. -- **`From`** - Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `ReplyTo` takes a list of addresses. -- **`Sensitive`** - Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). -- **`Tracking`** - What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). -- **`CaptureEngagement`** - Deprecated. Set `Tracking = new EmailTracking { Engagement = false }` instead. It still works, but a send that sets both `CaptureEngagement` and `Tracking` gets an error. +- **`TemplateKey`** – Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. +- **`Text`** – If you leave it out, PostHog makes it from `Html`. +- **`From`** – Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `ReplyTo` takes a list of addresses. +- **`Sensitive`** – Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). +- **`Tracking`** – What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). +- **`CaptureEngagement`** – Deprecated. Set `Tracking = new EmailTracking { Engagement = false }` instead. It still works, but a send that sets both `CaptureEngagement` and `Tracking` gets an error. `SendTemplateEmailOptions` sends a template made in PostHog, and `SendCodeEmailOptions` sends your content. Content from your code takes no template data, because PostHog renders nothing. See [content from your code](/docs/workflows/transactional-emails/api#send-content-from-your-code) for each field and its checks. @@ -227,9 +227,9 @@ public async Task SendPasswordResetAsync(User user, string html, string | `EmailTracking.None` | Off | Off | Off | | `new EmailTracking { ... }` | `Opens`, on by default | `Clicks`, default follows `Sensitive` | `Engagement`, on by default | -- **Opens** - PostHog adds an open pixel to the email, so it can count opens. -- **Clicks** - PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. -- **Engagement events** - PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. +- **Opens** – PostHog adds an open pixel to the email, so it can count opens. +- **Clicks** – PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. +- **Engagement events** – PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. If you leave out clicks, PostHog tracks them only when the email isn't sensitive, because a login link or a reset token in a tracked link would go through a redirect. Set `Sensitive = false` for an email that carries no secrets. For a sensitive email with links that are safe to rewrite, set `Tracking = new EmailTracking { Clicks = true }`. A clicks value in `Tracking` wins over `Sensitive`. diff --git a/contents/docs/workflows/transactional-emails/elixir.mdx b/contents/docs/workflows/transactional-emails/elixir.mdx index 94ba281c2196..e8ebf7c2171c 100644 --- a/contents/docs/workflows/transactional-emails/elixir.mdx +++ b/contents/docs/workflows/transactional-emails/elixir.mdx @@ -9,7 +9,7 @@ The [Elixir SDK](/docs/libraries/elixir) sends transactional emails from version
Prompt for your coding agent -Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that are easy to get wrong. +Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that agents often get wrong. ```text Add PostHog transactional email to this Elixir app with the posthog Hex package. @@ -164,8 +164,8 @@ Add the job where your app creates the reset request: Pass the distinct ID that you pass to `identify()` as `distinct_id`, as a string. This is usually your user's ID. PostHog then records the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on that user's person, in the same timeline as their product events. The SDK doesn't take it from `PostHog.set_context/2`. -- **No distinct ID** - PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. -- **Not a user yet** - For an invite, or an email address check before the account exists, leave `distinct_id` out. +- **No distinct ID** – PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. +- **Not a user yet** – For an invite, or an email address check before the account exists, leave `distinct_id` out. ## Send content from your code @@ -188,12 +188,12 @@ def send_password_reset(user, html, reset_request_id) do end ``` -- **`template_key`** - Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. -- **`text`** - If you leave it out, PostHog makes it from `html`. -- **`from`** - Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `reply_to` takes one address or a list. -- **`sensitive`** - Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). -- **`tracking`** - What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). -- **`capture_engagement`** - Deprecated. Pass `tracking: %{engagement: false}` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. +- **`template_key`** – Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. +- **`text`** – If you leave it out, PostHog makes it from `html`. +- **`from`** – Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `reply_to` takes one address or a list. +- **`sensitive`** – Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). +- **`tracking`** – What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). +- **`capture_engagement`** – Deprecated. Pass `tracking: %{engagement: false}` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. A send has `template_id` or `template_key`, not both. Content from your code takes no `template_data`, because PostHog renders nothing. See [content from your code](/docs/workflows/transactional-emails/api#send-content-from-your-code) for each field and its checks. @@ -210,9 +210,9 @@ A send has `template_id` or `template_key`, not both. Content from your code tak | `tracking: false` | Off | Off | Off | | A map, such as `tracking: %{clicks: true}` | `:opens`, on by default | `:clicks`, default follows `sensitive` | `:engagement`, on by default | -- **Opens** - PostHog adds an open pixel to the email, so it can count opens. -- **Clicks** - PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. -- **Engagement events** - PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. +- **Opens** – PostHog adds an open pixel to the email, so it can count opens. +- **Clicks** – PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. +- **Engagement events** – PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. If you leave out clicks, PostHog tracks them only when the email isn't sensitive, because a login link or a reset token in a tracked link would go through a redirect. Pass `sensitive: false` for an email that carries no secrets. For a sensitive email with links that are safe to rewrite, pass `tracking: %{clicks: true}`. A clicks value in `tracking` wins over `sensitive`. diff --git a/contents/docs/workflows/transactional-emails/go.mdx b/contents/docs/workflows/transactional-emails/go.mdx index 91845d5fc2e5..06f2670c2bb2 100644 --- a/contents/docs/workflows/transactional-emails/go.mdx +++ b/contents/docs/workflows/transactional-emails/go.mdx @@ -9,7 +9,7 @@ The [Go SDK](/docs/libraries/go) sends transactional emails from version 1.40.0.
Prompt for your coding agent -Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that are easy to get wrong. +Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that agents often get wrong. ```text Add PostHog transactional email to this Go app with github.com/posthog/posthog-go. @@ -161,8 +161,8 @@ Register the worker with `river.AddWorker(workers, &PasswordResetWorker{PostHog: Pass the distinct ID that you pass to `identify()` as `DistinctID`. This is usually your user's ID. PostHog then records the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on that user's person, in the same timeline as their product events. -- **No distinct ID** - PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. -- **Not a user yet** - For an invite, or an email address check before the account exists, leave `DistinctID` out. +- **No distinct ID** – PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. +- **Not a user yet** – For an invite, or an email address check before the account exists, leave `DistinctID` out. ## Send content from your code @@ -186,12 +186,12 @@ func sendPasswordReset(ctx context.Context, client posthog.Client, userID, email } ``` -- **`TemplateKey`** - Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. -- **`Text`** - If you leave it out, PostHog makes it from `HTML`. -- **`From`** - Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `ReplyTo` takes a list of addresses. -- **`Sensitive`** - Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). -- **`Tracking`** - What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). -- **`CaptureEngagement`** - Deprecated. Set `Tracking: posthog.EmailTrackingWith(posthog.EmailTrackingOptions{Engagement: posthog.Ptr(false)})` instead. It still works, but a send that sets both `CaptureEngagement` and `Tracking` gets an error. +- **`TemplateKey`** – Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. +- **`Text`** – If you leave it out, PostHog makes it from `HTML`. +- **`From`** – Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `ReplyTo` takes a list of addresses. +- **`Sensitive`** – Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). +- **`Tracking`** – What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). +- **`CaptureEngagement`** – Deprecated. Set `Tracking: posthog.EmailTrackingWith(posthog.EmailTrackingOptions{Engagement: posthog.Ptr(false)})` instead. It still works, but a send that sets both `CaptureEngagement` and `Tracking` gets an error. A `TemplateEmail` sends a template made in PostHog, and a `CodeEmail` sends your content. A `CodeEmail` takes no template data, because PostHog renders nothing. See [content from your code](/docs/workflows/transactional-emails/api#send-content-from-your-code) for each field and its checks. @@ -208,9 +208,9 @@ A `TemplateEmail` sends a template made in PostHog, and a `CodeEmail` sends your | `posthog.EmailTrackingOff()` | Off | Off | Off | | `posthog.EmailTrackingWith(...)` | `Opens`, on by default | `Clicks`, default follows `Sensitive` | `Engagement`, on by default | -- **Opens** - PostHog adds an open pixel to the email, so it can count opens. -- **Clicks** - PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. -- **Engagement events** - PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. +- **Opens** – PostHog adds an open pixel to the email, so it can count opens. +- **Clicks** – PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. +- **Engagement events** – PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. If you leave out clicks, PostHog tracks them only when the email isn't sensitive, because a login link or a reset token in a tracked link would go through a redirect. Set `Sensitive: posthog.Ptr(false)` for an email that carries no secrets. For a sensitive email with links that are safe to rewrite, set `Tracking: posthog.EmailTrackingWith(posthog.EmailTrackingOptions{Clicks: posthog.Ptr(true)})`. A clicks value in `Tracking` wins over `Sensitive`. diff --git a/contents/docs/workflows/transactional-emails/index.mdx b/contents/docs/workflows/transactional-emails/index.mdx index bacbbf6e3107..d10c1c849c3d 100644 --- a/contents/docs/workflows/transactional-emails/index.mdx +++ b/contents/docs/workflows/transactional-emails/index.mdx @@ -110,11 +110,11 @@ Each SDK page also has a prompt that you can paste into your agent. The prompt s Each transactional email has its own page in **Transactional**, with these tabs: -- **Design** - Edit the email and its sender -- **Metrics** - See the metrics for its sends -- **Sends** - See each send, with its log messages -- **Versions** - See each version that you published -- **API** - Copy the template ID and an example send +- **Design** – Edit the email and its sender +- **Metrics** – See the metrics for its sends +- **Sends** – See each send, with its log messages +- **Versions** – See each version that you published +- **API** – Copy the template ID and an example send @@ -130,9 +130,9 @@ You can also make a template transactional with the API. Call `POST /api/project ### Edit a transactional email -- **Sender** - The **Design** tab shows who the email comes from. Click **Edit sender** to choose a different verified sender, or to set a display name, a from address, or a reply-to address. The from address must be on the sender's verified domain. To use more than one reply-to address, separate them with commas. -- **Drafts** - Your edits save as a draft. Sends use the live version of the email until you click **Publish**. Each publish adds a version on the **Versions** tab. To throw away your edits, click **Discard draft**. -- **Checks** - PostHog doesn't publish a change that would stop the email from sending, such as an empty subject or body, or a marketing category. +- **Sender** – The **Design** tab shows who the email comes from. Click **Edit sender** to choose a different verified sender, or to set a display name, a from address, or a reply-to address. The from address must be on the sender's verified domain. To use more than one reply-to address, separate them with commas. +- **Drafts** – Your edits save as a draft. Sends use the live version of the email until you click **Publish**. Each publish adds a version on the **Versions** tab. To throw away your edits, click **Discard draft**. +- **Checks** – PostHog doesn't publish a change that would stop the email from sending, such as an empty subject or body, or a marketing category. ### Protect sensitive data @@ -155,11 +155,11 @@ To give a service a key that can read statuses but not send, set **Transactional ## What a send needs -- **Template ID** - The email's **API** tab shows it. -- **Recipient** - One email address for each send. -- **Template data** - The values for the template's `{{ variables.* }}` placeholders. The template can't read person properties, so pass all the data that the email needs. -- **Distinct ID** - The distinct ID that you pass to `identify()`. PostHog records the send and its [engagement events](/docs/workflows/engagement-events) on this person. Without it, PostHog records the email on a person whose distinct ID is the email address, which is a different person from your user, unless you identify users by their email address. Leave it out for someone who isn't a user yet, such as an invitee. -- **Idempotency key** - It makes a retry safe. See [retry safely](#retry-safely-with-an-idempotency-key). +- **Template ID** – The email's **API** tab shows it. +- **Recipient** – One email address for each send. +- **Template data** – The values for the template's `{{ variables.* }}` placeholders. The template can't read person properties, so pass all the data that the email needs. +- **Distinct ID** – The distinct ID that you pass to `identify()`. PostHog records the send and its [engagement events](/docs/workflows/engagement-events) on this person. Without it, PostHog records the email on a person whose distinct ID is the email address, which is a different person from your user, unless you identify users by their email address. Leave it out for someone who isn't a user yet, such as an invitee. +- **Idempotency key** – It makes a retry safe. See [retry safely](#retry-safely-with-an-idempotency-key). In the template, use the same names that you send in the template data: @@ -185,10 +185,10 @@ Build the key from the record that caused the email. A random value or a timesta A key is 1 to 255 visible ASCII characters, with no spaces. Keys are separate for each project. -- **Same key, same request** - PostHog sends no new email. It returns `202` with the `message_id` of the first send and `"replayed": true`. PostHog remembers a key for 24 hours after it queues the email. -- **Same key, different request** - PostHog returns `422` with the code `idempotency_key_reused`. Two requests are the same when they have the same template, `to`, `distinct_id`, and `message_data`. -- **First request not finished** - PostHog returns `409` with the code `idempotency_key_in_flight`. Wait a few seconds, then retry with the same key. -- **Refused request** - When PostHog refuses a send, for example with `recipient_suppressed` or `quota_limited`, the key stays unused. You can send the same key again later. +- **Same key, same request** – PostHog sends no new email. It returns `202` with the `message_id` of the first send and `"replayed": true`. PostHog remembers a key for 24 hours after it queues the email. +- **Same key, different request** – PostHog returns `422` with the code `idempotency_key_reused`. Two requests are the same when they have the same template, `to`, `distinct_id`, and `message_data`. +- **First request not finished** – PostHog returns `409` with the code `idempotency_key_in_flight`. Wait a few seconds, then retry with the same key. +- **Refused request** – When PostHog refuses a send, for example with `recipient_suppressed` or `quota_limited`, the key stays unused. You can send the same key again later. ## Errors @@ -252,11 +252,11 @@ A status only moves forward. For example, a `delivered` email never goes back to `reason` is `null` unless the email failed or bounced. A failed email has one of these reasons: -- `provider_error` or `provider_rejected` - The email provider refused the email. -- `recipient_suppressed`, `recipient_unreachable`, or `missing_recipient` - PostHog skipped the email because of the recipient. -- `sending_paused` or `sending_suspended` - Sending is paused for the template or suspended for the project. Check the **Reputation** tab in Workflows. -- `canceled` or `workflow_unavailable` - The send stopped before the email went out. -- Another value, such as `timeout` - The send failed with this kind of error. New values can appear. +- `provider_error` or `provider_rejected` – The email provider refused the email. +- `recipient_suppressed`, `recipient_unreachable`, or `missing_recipient` – PostHog skipped the email because of the recipient. +- `sending_paused` or `sending_suspended` – Sending is paused for the template or suspended for the project. Check the **Reputation** tab in Workflows. +- `canceled` or `workflow_unavailable` – The send stopped before the email went out. +- Another value, such as `timeout` – The send failed with this kind of error. New values can appear. A `failed` email can still change to `sent` if PostHog retries the send. @@ -280,9 +280,9 @@ Work down this list. Each step rules out one cause. ## How transactional emails behave -- **No opt-outs or frequency caps** - Transactional emails ignore marketing [opt-outs](/docs/workflows/opt-outs) and the [frequency cap](/docs/workflows/frequency-cap). Use them only for emails that a person needs, never for marketing. -- **Suppression still applies** - PostHog doesn't send to an address on your suppression list. -- **Billing and quotas** - Transactional emails are billed and limited by quota like other workflow emails. See [pricing](/pricing) and [billing limits](/docs/billing/limits-alerts). -- **Sending allowance** - Transactional emails count toward your project's [sending allowance](/docs/workflows/sending-reputation). When your project reaches its hourly or daily cap, emails wait in the queue until the allowance frees up. If a transactional email gets too many bounces or complaints, PostHog can pause its sending, and sends return `sending_paused`. -- **Engagement events** - If you [capture email engagement events](/docs/workflows/engagement-events), PostHog records them for transactional emails on the person whose distinct ID you send. An email from your code tracks clicks only when it isn't [sensitive](/docs/workflows/transactional-emails/api#sensitive-emails), or when [`tracking`](/docs/workflows/transactional-emails/api#open-and-click-tracking) turns them on. -- **Where to see sends** - [Read the status](#check-an-emails-status) of an email with its `message_id`. The email's **Sends** tab lists each send with its log messages. The **Metrics** tab shows their metrics. Each send also appears on the recipient's person. +- **No opt-outs or frequency caps** – Transactional emails ignore marketing [opt-outs](/docs/workflows/opt-outs) and the [frequency cap](/docs/workflows/frequency-cap). Use them only for emails that a person needs, never for marketing. +- **Suppression still applies** – PostHog doesn't send to an address on your suppression list. +- **Billing and quotas** – Transactional emails are billed and limited by quota like other workflow emails. See [pricing](/pricing) and [billing limits](/docs/billing/limits-alerts). +- **Sending allowance** – Transactional emails count toward your project's [sending allowance](/docs/workflows/sending-reputation). When your project reaches its hourly or daily cap, emails wait in the queue until the allowance frees up. If a transactional email gets too many bounces or complaints, PostHog can pause its sending, and sends return `sending_paused`. +- **Engagement events** – If you [capture email engagement events](/docs/workflows/engagement-events), PostHog records them for transactional emails on the person whose distinct ID you send. An email from your code tracks clicks only when it isn't [sensitive](/docs/workflows/transactional-emails/api#sensitive-emails), or when [`tracking`](/docs/workflows/transactional-emails/api#open-and-click-tracking) turns them on. +- **Where to see sends** – [Read the status](#check-an-emails-status) of an email with its `message_id`. The email's **Sends** tab lists each send with its log messages. The **Metrics** tab shows their metrics. Each send also appears on the recipient's person. diff --git a/contents/docs/workflows/transactional-emails/node.mdx b/contents/docs/workflows/transactional-emails/node.mdx index 8b53ce1b614d..88ad1636a53f 100644 --- a/contents/docs/workflows/transactional-emails/node.mdx +++ b/contents/docs/workflows/transactional-emails/node.mdx @@ -9,7 +9,7 @@ The [Node.js SDK](/docs/libraries/node) sends transactional emails from version
Prompt for your coding agent -Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that are easy to get wrong. +Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that agents often get wrong. ```text Add PostHog transactional email to this Node.js app with posthog-node. @@ -143,8 +143,8 @@ new Worker( Pass the distinct ID that you pass to `identify()` as `distinctId`. This is usually your user's ID. PostHog then records the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on that user's person, in the same timeline as their product events. -- **No distinct ID** - PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. -- **Not a user yet** - For an invite, or an email address check before the account exists, leave `distinctId` out. +- **No distinct ID** – PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. +- **Not a user yet** – For an invite, or an email address check before the account exists, leave `distinctId` out. ## Send content from your code @@ -172,13 +172,13 @@ export async function sendPasswordReset({ userId, email, firstName, resetLink, r } ``` -- **`templateKey`** - Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. -- **`react`** - A React Email element. See [send React Email components](/docs/workflows/transactional-emails/react-email) for the install, the errors, and Next.js. -- **`text`** - If you leave it out, PostHog makes it from `html`, or the SDK makes it from the rendered `react` element. -- **`from`** - Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `replyTo` takes one address or a list. -- **`sensitive`** - Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). -- **`tracking`** - What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). -- **`captureEngagement`** - Deprecated. Pass `tracking: { engagement: false }` instead. It still works, but a send that sets both `captureEngagement` and `tracking` gets an error. +- **`templateKey`** – Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. +- **`react`** – A React Email element. See [send React Email components](/docs/workflows/transactional-emails/react-email) for the install, the errors, and Next.js. +- **`text`** – If you leave it out, PostHog makes it from `html`, or the SDK makes it from the rendered `react` element. +- **`from`** – Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `replyTo` takes one address or a list. +- **`sensitive`** – Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). +- **`tracking`** – What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). +- **`captureEngagement`** – Deprecated. Pass `tracking: { engagement: false }` instead. It still works, but a send that sets both `captureEngagement` and `tracking` gets an error. A send has `templateId` or `templateKey`, not both. Content from your code takes no `templateData`, because PostHog renders nothing. See [content from your code](/docs/workflows/transactional-emails/api#send-content-from-your-code) for each field and its checks. @@ -195,9 +195,9 @@ A send has `templateId` or `templateKey`, not both. Content from your code takes | `tracking: false` | Off | Off | Off | | An object, such as `tracking: { clicks: true }` | `opens`, on by default | `clicks`, default follows `sensitive` | `engagement`, on by default | -- **Opens** - PostHog adds an open pixel to the email, so it can count opens. -- **Clicks** - PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. -- **Engagement events** - PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. +- **Opens** – PostHog adds an open pixel to the email, so it can count opens. +- **Clicks** – PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. +- **Engagement events** – PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. If you leave out clicks, PostHog tracks them only when the email isn't sensitive, because a login link or a reset token in a tracked link would go through a redirect. Pass `sensitive: false` for an email that carries no secrets. For a sensitive email with links that are safe to rewrite, pass `tracking: { clicks: true }`. A clicks value in `tracking` wins over `sensitive`. diff --git a/contents/docs/workflows/transactional-emails/php.mdx b/contents/docs/workflows/transactional-emails/php.mdx index 7fb4ac71c9d0..a6f0efdf3897 100644 --- a/contents/docs/workflows/transactional-emails/php.mdx +++ b/contents/docs/workflows/transactional-emails/php.mdx @@ -9,7 +9,7 @@ The [PHP SDK](/docs/libraries/php) sends transactional emails from version 4.22.
Prompt for your coding agent -Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that are easy to get wrong. +Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that agents often get wrong. ```text Add PostHog transactional email to this PHP app with posthog/posthog-php. @@ -161,8 +161,8 @@ SendPasswordResetEmail::dispatch((string) $user->id, $user->email, $user->firstN Pass the distinct ID that you pass to `identify()` as `distinct_id`. This is usually your user's ID. PostHog then records the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on that user's person, in the same timeline as their product events. -- **No distinct ID** - PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. -- **Not a user yet** - For an invite, or an email address check before the account exists, leave `distinct_id` out. +- **No distinct ID** – PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. +- **Not a user yet** – For an invite, or an email address check before the account exists, leave `distinct_id` out. ## Send content from your code @@ -184,12 +184,12 @@ function sendPasswordReset(User $user, string $html, string $resetRequestId): st } ``` -- **`template_key`** - Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. -- **`text`** - If you leave it out, PostHog makes it from `html`. -- **`from`** - Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `reply_to` takes one address or a list. -- **`sensitive`** - Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). -- **`tracking`** - What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). -- **`capture_engagement`** - Deprecated. Pass `'tracking' => ['engagement' => false]` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. +- **`template_key`** – Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. +- **`text`** – If you leave it out, PostHog makes it from `html`. +- **`from`** – Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `reply_to` takes one address or a list. +- **`sensitive`** – Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). +- **`tracking`** – What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). +- **`capture_engagement`** – Deprecated. Pass `'tracking' => ['engagement' => false]` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. A send has `template_id` or `template_key`, not both. Content from your code takes no `template_data`, because PostHog renders nothing. See [content from your code](/docs/workflows/transactional-emails/api#send-content-from-your-code) for each field and its checks. @@ -206,9 +206,9 @@ A send has `template_id` or `template_key`, not both. Content from your code tak | `'tracking' => false` | Off | Off | Off | | An array, such as `'tracking' => ['clicks' => true]` | `'opens'`, on by default | `'clicks'`, default follows `sensitive` | `'engagement'`, on by default | -- **Opens** - PostHog adds an open pixel to the email, so it can count opens. -- **Clicks** - PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. -- **Engagement events** - PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. +- **Opens** – PostHog adds an open pixel to the email, so it can count opens. +- **Clicks** – PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. +- **Engagement events** – PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. If you leave out clicks, PostHog tracks them only when the email isn't sensitive, because a login link or a reset token in a tracked link would go through a redirect. Pass `'sensitive' => false` for an email that carries no secrets. For a sensitive email with links that are safe to rewrite, pass `'tracking' => ['clicks' => true]`. A clicks value in `tracking` wins over `sensitive`. diff --git a/contents/docs/workflows/transactional-emails/python.mdx b/contents/docs/workflows/transactional-emails/python.mdx index e08b1c8042fc..b84b44d805ce 100644 --- a/contents/docs/workflows/transactional-emails/python.mdx +++ b/contents/docs/workflows/transactional-emails/python.mdx @@ -9,7 +9,7 @@ The [Python SDK](/docs/libraries/python) sends transactional emails from version
Prompt for your coding agent -Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that are easy to get wrong. +Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that agents often get wrong. ```text Add PostHog transactional email to this Python app with the posthog package. @@ -165,8 +165,8 @@ def send_password_reset_email(self, user_id: str, email: str, first_name: str, r Pass the distinct ID that you pass to `identify()` as `distinct_id`. This is usually your user's ID. PostHog then records the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on that user's person, in the same timeline as their product events. -- **No distinct ID** - PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. -- **Not a user yet** - For an invite, or an email address check before the account exists, leave `distinct_id` out. +- **No distinct ID** – PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. +- **Not a user yet** – For an invite, or an email address check before the account exists, leave `distinct_id` out. ## Send content from your code @@ -204,12 +204,12 @@ async def send_password_reset(user_id: str, email: str, html: str, reset_request -- **`template_key`** - Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. -- **`text`** - If you leave it out, PostHog makes it from `html`. -- **`from_`** - The sender. It has a trailing underscore because `from` is a Python keyword. It needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `reply_to` takes one address or a list. -- **`sensitive`** - Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). -- **`tracking`** - What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). -- **`capture_engagement`** - Deprecated. Pass `tracking={"engagement": False}` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. +- **`template_key`** – Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. +- **`text`** – If you leave it out, PostHog makes it from `html`. +- **`from_`** – The sender. It has a trailing underscore because `from` is a Python keyword. It needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `reply_to` takes one address or a list. +- **`sensitive`** – Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). +- **`tracking`** – What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). +- **`capture_engagement`** – Deprecated. Pass `tracking={"engagement": False}` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. A send has `template_id` or `template_key`, not both, or it raises `TypeError`. Content from your code takes no `template_data`, because PostHog renders nothing. See [content from your code](/docs/workflows/transactional-emails/api#send-content-from-your-code) for each field and its checks. @@ -226,9 +226,9 @@ A send has `template_id` or `template_key`, not both, or it raises `TypeError`. | `tracking=False` | Off | Off | Off | | A dict, such as `tracking={"clicks": True}` | `"opens"`, on by default | `"clicks"`, default follows `sensitive` | `"engagement"`, on by default | -- **Opens** - PostHog adds an open pixel to the email, so it can count opens. -- **Clicks** - PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. -- **Engagement events** - PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. +- **Opens** – PostHog adds an open pixel to the email, so it can count opens. +- **Clicks** – PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. +- **Engagement events** – PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. If you leave out clicks, PostHog tracks them only when the email isn't sensitive, because a login link or a reset token in a tracked link would go through a redirect. Pass `sensitive=False` for an email that carries no secrets. For a sensitive email with links that are safe to rewrite, pass `tracking={"clicks": True}`. A clicks value in `tracking` wins over `sensitive`. diff --git a/contents/docs/workflows/transactional-emails/ruby.mdx b/contents/docs/workflows/transactional-emails/ruby.mdx index c9d4a4ad382b..a33025f4b975 100644 --- a/contents/docs/workflows/transactional-emails/ruby.mdx +++ b/contents/docs/workflows/transactional-emails/ruby.mdx @@ -10,7 +10,7 @@ The [Ruby SDK](/docs/libraries/ruby) sends transactional emails from version 3.3 Prompt for your coding agent -Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that are easy to get wrong. +Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that agents often get wrong. ```text Add PostHog transactional email to this Ruby app with the posthog-ruby gem. @@ -146,8 +146,8 @@ end Pass the distinct ID that you pass to `identify()` as `distinct_id`. This is usually your user's ID. PostHog then records the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on that user's person, in the same timeline as their product events. -- **No distinct ID** - PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. -- **Not a user yet** - For an invite, or an email address check before the account exists, leave `distinct_id` out. +- **No distinct ID** – PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. +- **Not a user yet** – For an invite, or an email address check before the account exists, leave `distinct_id` out. ## Send content from your code @@ -168,12 +168,12 @@ def send_password_reset(user, html, reset_request_id) end ``` -- **`template_key`** - Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. -- **`text`** - If you leave it out, PostHog makes it from `html`. -- **`from`** - Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `reply_to` takes one address or a list. -- **`sensitive`** - Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). -- **`tracking`** - What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). -- **`capture_engagement`** - Deprecated. Pass `tracking: { engagement: false }` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. +- **`template_key`** – Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. +- **`text`** – If you leave it out, PostHog makes it from `html`. +- **`from`** – Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. `reply_to` takes one address or a list. +- **`sensitive`** – Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). +- **`tracking`** – What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). +- **`capture_engagement`** – Deprecated. Pass `tracking: { engagement: false }` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. A send has `template_id` or `template_key`, not both, or it raises `ArgumentError`. Content from your code takes no `template_data`, because PostHog renders nothing. See [content from your code](/docs/workflows/transactional-emails/api#send-content-from-your-code) for each field and its checks. @@ -192,9 +192,9 @@ When migrating providers, set the client option `migrated_from`, for example `mi | `tracking: false` | Off | Off | Off | | A hash, such as `tracking: { clicks: true }` | `opens`, on by default | `clicks`, default follows `sensitive` | `engagement`, on by default | -- **Opens** - PostHog adds an open pixel to the email, so it can count opens. -- **Clicks** - PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. -- **Engagement events** - PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. +- **Opens** – PostHog adds an open pixel to the email, so it can count opens. +- **Clicks** – PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. +- **Engagement events** – PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. If you leave out clicks, PostHog tracks them only when the email isn't sensitive, because a login link or a reset token in a tracked link would go through a redirect. Pass `sensitive: false` for an email that carries no secrets. For a sensitive email with links that are safe to rewrite, pass `tracking: { clicks: true }`. A clicks value in `tracking` wins over `sensitive`. diff --git a/contents/docs/workflows/transactional-emails/rust.mdx b/contents/docs/workflows/transactional-emails/rust.mdx index c6c71dc5593a..0ceb5eeb2458 100644 --- a/contents/docs/workflows/transactional-emails/rust.mdx +++ b/contents/docs/workflows/transactional-emails/rust.mdx @@ -9,7 +9,7 @@ The [Rust SDK](/docs/libraries/rust) sends transactional emails from version 0.3
Prompt for your coding agent -Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that are easy to get wrong. +Paste this into Claude Code, Cursor, or another coding agent. It sends the agent to this page and gives it the rules that agents often get wrong. ```text Add PostHog transactional email to this Rust app with the posthog-rs crate. @@ -150,8 +150,8 @@ pub async fn run_password_reset_job( Pass the distinct ID that you pass to `identify()` as `distinct_id`. This is usually your user's ID. PostHog then records the email's [engagement events](/docs/workflows/engagement-events), such as sent, opened, and clicked, on that user's person, in the same timeline as their product events. -- **No distinct ID** - PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. -- **Not a user yet** - For an invite, or an email address check before the account exists, leave `distinct_id` out. +- **No distinct ID** – PostHog records the email on a person whose distinct ID is the email address. That's a different person from your user, unless your app identifies users by their email address. +- **Not a user yet** – For an invite, or an email address check before the account exists, leave `distinct_id` out. ## Send content from your code @@ -180,12 +180,12 @@ async fn send_password_reset( } ``` -- **Template key** - Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. -- **`text`** - If you leave it out, PostHog makes it from `html`. -- **`from`** - Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. Call `reply_to` once for each reply-to address. -- **`sensitive`** - Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). -- **`tracking`** - What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). -- **`capture_engagement`** - Deprecated. Call `.tracking(EmailTrackingOptions::new().engagement(false))` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. +- **Template key** – Names the email, such as `password-reset`. PostHog groups the email's metrics and sends under this key, so use the same key every time you send this email. +- **`text`** – If you leave it out, PostHog makes it from `html`. +- **`from`** – Needs an address on a domain that your project verified. Without it, PostHog uses the project's default sender. Call `reply_to` once for each reply-to address. +- **`sensitive`** – Marks the email as sensitive, for login links, reset tokens, or codes. If you leave it out, the email takes the setting of the template with the same key, or is sensitive when no template has that key. See [sensitive emails](/docs/workflows/transactional-emails/api#sensitive-emails). +- **`tracking`** – What PostHog tracks for the email. If you leave it out, PostHog tracks opens and captures engagement events, and tracks clicks only when the email isn't sensitive. See [open and click tracking](#open-and-click-tracking). +- **`capture_engagement`** – Deprecated. Call `.tracking(EmailTrackingOptions::new().engagement(false))` instead. It still works, but a send that sets both `capture_engagement` and `tracking` gets an error. A `TemplateEmail` sends a template made in PostHog, and a `CodeEmail` sends your content. A `CodeEmail` takes no template data, because PostHog renders nothing. See [content from your code](/docs/workflows/transactional-emails/api#send-content-from-your-code) for each field and its checks. @@ -202,9 +202,9 @@ A `TemplateEmail` sends a template made in PostHog, and a `CodeEmail` sends your | `.tracking(EmailTracking::Off)` | Off | Off | Off | | `EmailTrackingOptions::new()` with its builder methods | `opens`, on by default | `clicks`, default follows `sensitive` | `engagement`, on by default | -- **Opens** - PostHog adds an open pixel to the email, so it can count opens. -- **Clicks** - PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. -- **Engagement events** - PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. +- **Opens** – PostHog adds an open pixel to the email, so it can count opens. +- **Clicks** – PostHog rewrites the email's links to go through a tracking redirect, so it can count clicks. +- **Engagement events** – PostHog captures the email's engagement events on the recipient's person. If the project's **Capture email engagement events** setting is off, PostHog captures nothing, whatever the send says. If you leave out clicks, PostHog tracks them only when the email isn't sensitive, because a login link or a reset token in a tracked link would go through a redirect. Call `.sensitive(false)` for an email that carries no secrets. For a sensitive email with links that are safe to rewrite, call `.tracking(EmailTrackingOptions::new().clicks(true))`. A clicks value in `tracking` wins over `sensitive`.