Integrate transactional email

Ask about this Page
Copy for AI
View as Markdown

Send transactional Customer and Order emails from commercetools through an external email service provider.

Learn more about integrations in our self-paced Integration patterns module.
This guide covers one-way, event-driven delivery of account, token, Order, shipment, and return-information emails. It does not cover marketing campaigns or Customer synchronization with a customer relationship management platform. Feeding a customer data platform is an export pipeline covered by Integrate an analytics destination.
Before you design the integration, define ownership, volume, latency, failure tolerance, and recovery targets. For a planning framework, see Integration planning and patterns.
The transactional email integration template is a development scaffold. The assessment in this guide uses commit a7f1856, inspected on 25 August 2026. The scaffold routes Messages and manages a Subscription, but its provider method only logs email data. Before production use, implement the provider API call and its delivery and recovery behavior. Also add durable deduplication, localization, safe logging, and the tests described in this guide.

Define transactional email requirements

Record each email as a separate requirement. Include its recipient, trigger, template, locale source and fallback, acceptable delivery delay, and tolerance for a missing or duplicate email. Also decide how long delivery records must remain available for support and audit purposes.

Use the Customer Messages and Cart and Order Messages references to choose only the triggers required by your use cases. Apply these decisions to the selected triggers:
  • Decide whether Customer creation sends an email for every Customer or only for Customers created through specific Stores.
  • Define the token lifetime, delivery policy, and verification URL for email verification and password reset. Treat a missing token email as a blocking failure and protect the token as a secret.
  • Decide which Order creation flows send a confirmation. Include imported and recurring Order creation when those flows are in scope.
  • Allowlist the Order and shipment states that warrant a customer email.
  • Describe return-information changes as return updates. Use an authoritative payment event when the email must state that money was refunded.
For Store-specific Customers, use the corresponding Customer in Store endpoints for email verification and password reset. Grant only the required Store-scoped Customer permissions.

Record special requirements separately because they can change the application design:

  • Define how multi-Store or multi-brand integrations select sender identities and templates.
  • Define B2B recipients and any Business Unit-specific templates.
  • Verify attachment support and size limits with the provider.
  • Use a separate authenticated service application when provider bounce or complaint webhooks must update commercetools.
  • Use a separate job application for batch or digest emails.

For each requirement, classify a duplicate and a missing email by business impact. This decision controls when the Connector acknowledges a Message and whether it needs durable deduplication.

Choose a Connector path

Understand the Connect application model

Connect runs applications without requiring you to manage their runtime infrastructure. Use an asynchronous event application for a transactional email integration. A Subscription delivers a Message after the commercetools change occurs. The application then calls the email provider.

Do not use an API Extension to send transactional email. An API Extension runs synchronously in a commercetools API request, so provider latency or an outage could delay or reject a Customer or Order operation.

Use an existing integration

Check the live Connect inventory in the Merchant Center and the Connect marketplace. Review each candidate's deployment and capability documentation instead of relying on its marketplace category.

For each candidate, verify the following before installation:

  • It is deployable through Connect and supports your Region.
  • Its contract includes the required Message types, templates, locale behavior, and provider.
  • Its acknowledgment, retry, deduplication, and permanent-failure policies match your requirements.
  • Its source, lifecycle behavior, security controls, tests, and operational runbook meet your production standards.

If configuration covers the requirements, use the existing Connector. This avoids maintaining a fork for a difference that a template mapping, state allowlist, or locale setting can resolve.

Create your own integration

If no existing Connector fits, choose one of these paths:

  1. Fork a source-available Connector when it already implements most of the required contract and has a specific code gap.
  2. Customize the transactional email integration template when you need its Subscription and routing scaffold.
  3. Build a new event application when the scaffold or an existing Connector would require broader changes than your own focused implementation.
The template is not a working provider integration. At the audited commit, it includes no provider SDK and no provider send call. Its sendMail method logs the sender, recipient, template ID, and template data instead. Treat the template as a starting point, not as production-ready software.

Configure the Connector and email provider

Configure the event application, provider account, and Subscription as one contract.

Configure the event application

Keep secrets such as the provider API key and sender credentials in secured configuration. Configure a template ID for each email and locale. Keep non-secret values, such as CTP_REGION, in standard configuration. The application route must match the connect.yaml endpoint. For example, the template declares and serves /mailSender; a mismatch prevents delivery requests from reaching the handler.
Grant the generated API Client only the permissions required by the selected flows. The required access depends on whether the Connector manages the Subscription, retrieves Customers or Orders, or creates Customer tokens. Use the canonical API scopes when configuring automatic API Client generation.
Declare these permissions through inheritAs.apiClient.scopes instead of supplying API Client credentials. Build the scope set from the enabled flows:
CapabilityScope
Create or update the Subscription in postDeploymanage_subscriptions
Retrieve an Order for an Order emailview_orders
Retrieve a Customer for a Customer emailview_customers
Create an email verification or password reset token in the handlermanage_customers, which replaces view_customers for that flow
Connect injects the event destination configuration. Build the Subscription destination from the values documented for automation scripts, and read the injected destination type instead of hardcoding it. Connect deploys to both Google Cloud and AWS Regions, and the message broker follows the Region. A Google Cloud Region uses Google Cloud Pub/Sub, and an AWS Region uses Amazon SNS. commercetools sends a test notification when a Subscription is created to confirm the destination is configured correctly, so a destination built for the wrong broker fails at creation rather than failing silently later. A Connector that assumes one broker therefore deploys in one Region and fails to register its Subscription in another. If a working destination later becomes undeliverable, the Subscription reports a ConfigurationError health status and delivery stops after 24 hours on production Projects, or 1 hour on development and staging Projects. Keep the destination and Region configuration consistent with CTP_REGION, and monitor Subscription health rather than treating silence as success.

Configure the email provider

Prefer templates that the provider hosts and that the Connector references by ID over rendering email markup in the Connector. Provider-hosted templates let non-technical teams change copy and layout without redeploying the Connector, and they keep the application a thin data-mapper. Render inside the Connector only when the provider offers no template feature or you require full control over the output.

Create the provider templates and define their required variables before implementing the mapping. Verify the sender address or domain with the provider. A provider can reject an unverified sender or accept the request without delivering the message to the inbox.

Use the provider transactional-send API and set a timeout that leaves enough time to acknowledge the event. Connect event applications have a 10-second acknowledgment deadline. Cold starts and provider latency consume that budget, as does Customer or Order retrieval. A default sandbox deployment can scale to zero and take approximately 15 seconds to start again, so it can exceed the acknowledgment deadline during testing.

Manage the Subscription lifecycle

The template uses Connect automation scripts to manage the Subscription lifecycle. Follow the documented postDeploy and preUndeploy contract, and keep the destination and selected Message types synchronized with the deployed event application.
The audited template uses this fixed Subscription key: ct-connect-email-delivery-subscription.
It deletes that Subscription before creating a replacement. This is repeatable, but it creates an availability gap and is not an atomic update. Replace that behavior with a get, create, or versioned update flow. Confirm the resulting Subscription after every deployment. A failed postDeploy can leave a healthy application with no incoming Messages. Duplicate lifecycle registrations can cause duplicate sends.
Use Subscription delivery health to define alerting and replay procedures for delivery errors and retry exhaustion.

Map Customer and Order Messages

The Subscription filter and handler router must contain the same explicit set of Message types. Broad resource subscriptions increase cost and can route unrelated Messages into email logic.

Map each email requirement to its Subscription filter value:

EmailresourceTypeIdMessage type
Customer registrationcustomerCustomerCreated
Email verificationcustomer-email-tokenCustomerEmailTokenCreated
Password resetcustomer-password-tokenCustomerPasswordTokenCreated
Order confirmationorderOrderCreated, OrderImported, or OrderCreatedFromRecurringOrder
Order state or cancellationorderOrderStateChanged
ShipmentorderOrderShipmentStateChanged
Return updateorderReturnInfoAdded or ReturnInfoSet
Compare the template's configured Message filter and handler router with the requirements. The audited template omits OrderCreatedFromRecurringOrderMessage. If recurring Order creation is in scope, add the Message type to both the Subscription filter and the handler router.
For ordinary Customer and Order Messages, use the Message as the event record. A Message contains a resource reference, and its event-specific payload can be omitted. Re-fetch the referenced resource when the template needs current data. To recover omitted payload data through the Messages API, first enable Messages for the Project. Handle payloadNotIncluded and failed lookups deliberately instead of building an incomplete email.

Re-fetching also has a consequence for state emails. Because asynchronous Messages are not ordered, the retrieved Order can already have a later state than the transition recorded by the Message. Choose and test one policy:

  • Use the transition represented by the Message to decide whether to send, then use the retrieved Order only for current template data.
  • Use the current retrieved state and intentionally collapse intermediate transitions.
Apply an allowlist to OrderStateChanged and OrderShipmentStateChanged. A handler that sends for every state Message can expose internal transitions and send too many emails. Map return-information Messages to return updates only. Use a separate, authoritative payment event if an email must claim that money was refunded.

Implement the event handler

Process each delivery in this order:

  1. Decode the envelope for the configured destination and validate the notification type.
  2. Validate and route the Message type. Positively acknowledge irrelevant, valid Messages instead of retrying them.
  3. Establish the delivery record or deduplication key required by the selected delivery policy.
  4. Retrieve the Customer or Order when full or current data is required.
  5. Select the locale and template, then map the resource to the provider variables.
  6. Call the provider or durably enqueue the send.
  7. Persist the provider acceptance or terminal failure state before returning the final acknowledgment.

Keep the Message-to-provider mapping as a pure function. Test recipient and template selection, locale handling, money formatting, date formatting, optional fields, and state allowlists without calling commercetools or the provider.

The provider boundary must use the provider SDK or transactional API. Classify provider rate limits, timeouts, and server errors as transient. Classify invalid recipients, invalid templates, and rejected sender configuration as permanent according to the provider contract. Retry transient failures. For permanent failures, record the failure, alert the operator, and positively acknowledge or quarantine the event so it does not retry forever.

Choose delivery and deduplication behavior

Subscriptions deliver Messages at least once, and email sends are usually not idempotent. Acknowledgment timing therefore changes the customer-visible result. Use Connect event application behavior and Subscription delivery guarantees to define acknowledgment, retry, monitoring, and replay procedures.
Connect acknowledges an event delivery only when the application responds with HTTP status 102, 200, 201, 202, or 204 within 10 seconds. Any other response or a missed deadline can cause redelivery.
These acknowledgment codes are specific to event applications. The same 202 returned from an API Extension rejects the triggering operation, so do not carry acknowledgment logic across the two application types.

The following diagrams compare the end-to-end flow for each policy. They show when the Connect application acknowledges the event, how failures affect delivery, and whether the Subscription can redeliver the Message.

PolicyFlowBenefitRisk and required control
At-most-onceAcknowledge, then retrieve and send.A Subscription redelivery cannot cause a second send.A crash or provider failure after the acknowledgment silently drops the email. Use only when this loss is acceptable or when a separate durable queue owns retries.
At-least-onceClaim a durable key; send or durably persist the work; record the result; then acknowledge.Transient failures can be retried.Redelivery can duplicate the send. Deduplicate with the provider's idempotency key, or a shared, durable sent-marker when the provider has none.

As a default, use at-most-once for Order and account confirmations, where a rare missed email is tolerable and a duplicate is merely unwelcome. Use at-least-once with deduplication for drop-intolerant emails, such as email verification and password reset, where a missing email blocks the Customer. Record the chosen policy for each email type.

The audited template uses the at-most-once flow: it returns 202 before validation, routing, resource retrieval, and provider work. Later failures are logged but cannot cause Subscription redelivery.

The audited template has no durable deduplication or provider-acceptance record, and it does not implement provider idempotency. Add these controls before changing the template to acknowledge after a send.

For at-least-once Message processing, make the send self-deduplicating instead of relying on a local store. Connect applications are stateless and run as isolated, autoscaled instances that cannot share memory or a filesystem. Prefer the provider's idempotency key, derived from a stable value such as resource.id and sequenceNumber, so the provider itself collapses a duplicate send. When the provider has no idempotency feature, record a sent-marker that every instance shares, such as a Custom Object keyed on the same value. Re-check that marker against live state before sending. An in-memory set is lost on restart and does not coordinate concurrent instances, so it does not deduplicate.

Design for an ambiguous provider result instead of assuming that every email is delivered exactly once. The provider can accept a request while the Connector times out before receiving the response. On retry, use the same provider idempotency key where supported, and reconcile an ambiguous result against provider activity before sending again.

Handle tokens, state transitions, localization, and personal data

Protect token flows

The email token Message definition specifies when an email token value is available. The password token Message definition provides the same contract for password reset. If the selected validity does not expose the value, redesign the initiating flow. Alternatively, create the token in the handler and email the returned value.
Creating a token in the handler can generate another token Message. Filter self-created token Messages by the creating API Client and test this behavior. The audited scaffold contains a self-created-client filter. Preserve and verify the filter when changing the token flow. Combine this filter with durable deduplication so redelivery does not create repeated tokens. For a replacement email token, decide whether to invalidate older tokens with CustomerCreateEmailToken. For a replacement password token, use CustomerCreatePasswordResetToken. Never log token values.

Preserve state meaning

Test the chosen transition policy with rapid consecutive Order updates. The email subject, body, and send condition must all refer to the same transition or intentionally selected current state. A retrieved current state must not silently change the meaning of an earlier Message.

Localize with a fallback

Select a locale from an agreed source, such as customer.locale or Store configuration. Define an ordered fallback for every localized value and provider template. The audited template hardcodes en-US for Order data and has no explicit locale fallback, so replace this behavior for a localized storefront.

Keep logs safe

Log a correlation key, Message type, resource ID, attempt, duration, provider result category, and deduplication state. Do not log full Message bodies, recipient addresses, names, token values, or template data. The audited scaffold logs several of these values and must be changed before production use.

Verify and operate the integration

Before deployment, add focused automated tests for the following behavior:

  • The route matches the connect.yaml endpoint, and each supported destination envelope is decoded correctly.
  • Every selected Message type routes to the intended template; unselected types are acknowledged without a send.
  • Resource retrieval handles omitted payloads, missing resources, and a resource that has advanced to a later state.
  • State allowlists, locale selection, and fallback behavior are deterministic.
  • The selected acknowledgment policy handles provider success, transient failure, permanent failure, timeout, and duplicate delivery.
  • Deduplication survives process restart and concurrent handling.
  • Token handling prevents self-trigger loops and repeated creation.
  • postDeploy creates or updates one Subscription, repeated deployment does not duplicate it, and preUndeploy removes it.
  • Logs and errors do not expose Customer data or tokens.

After deployment, verify the complete provider flow:

  1. Confirm that one Subscription exists with the expected key, destination, and Message filters.
  2. Trigger every supported Message through a representative Customer or Order operation.
  3. Confirm the Connector receives the event and finishes within the acknowledgment deadline.
  4. In the provider activity log, confirm the recipient, template, locale, and non-sensitive template data.
  5. Confirm inbox delivery using an approved live test recipient. Provider acceptance alone is not proof of delivery. A provider sandbox can accept a request while suppressing inbox delivery or restricting recipients.
  6. Redeliver the same event or replay an equivalent fixture. Confirm the result matches the selected duplicate policy.
  7. Force a transient provider failure and confirm retry or durable-queue recovery. Force a permanent failure and confirm alerting without an endless retry.
  8. Exercise rapid state transitions, a return update, a token flow, and locale fallback.

Monitor Subscription health, event age, processing duration, acknowledgment failures, provider response categories, retries, and deduplication conflicts. Also monitor permanent failures and provider delivery outcomes. Define how operators replay failed work without bypassing deduplication. Account for queue delivery delay and cold starts when setting an alert threshold. Subscription delivery has no guaranteed time frame and can be delayed by several minutes.

Troubleshoot observable symptoms

SymptomLikely causesChecks and resolution
No events reach the ConnectorThe Subscription is missing, postDeploy failed, delivery stopped after a configuration error, the Message filter is too narrow, or the destination and Region configuration disagree.Query the Subscription by key and inspect its health. Compare its destination and Message types with the deployed event application and CTP_REGION. Review lifecycle logs.
Delivery requests do not reach the handlerThe application route differs from the connect.yaml endpoint.Make the deployed route and endpoint identical. The template uses /mailSender.
Events retry or arrive lateThe handler misses the 10-second acknowledgment deadline because of a cold start, resource retrieval, or a slow provider call. A default sandbox cold start can take approximately 15 seconds. Subscription delivery can also be delayed by several minutes.Measure each stage. Reduce startup work, set provider timeouts, and durably enqueue before acknowledging when the design permits it. Account for the selected deployment environment in tests.
Logs show success, but no provider send existsThe provider method is still the scaffold logger, or the at-most-once handler acknowledged before a later failure.Confirm that a provider SDK or API call runs. Check provider activity, not only Connector logs. Implement recovery for work after an early acknowledgment.
The provider accepted the request, but no email reached the inboxThe provider is in sandbox mode, the recipient is restricted, the sender domain is unverified, or the provider suppressed or rejected delivery later.Inspect provider delivery events and sender verification. Test inbox delivery with an approved live configuration.
A Customer receives duplicate emailsAt-least-once delivery has no durable deduplication, the deduplication is only in memory, the provider result was ambiguous, or duplicate Subscriptions exist.Inspect the stable key and durable state, reconcile provider activity, reuse a provider idempotency key, and query Subscriptions for lifecycle duplicates.
An email disappears after a transient failureThe handler acknowledged before sending, so the Subscription does not redeliver.Use send-or-persist-before-ack with durable deduplication for drop-intolerant emails, or move retries to a durable queue.
A token link is emptyThe token validity exceeds 60 minutes and the handler expected the value in the Message.Use a validity of 60 minutes or less, or create and use a token in the handler.
Token emails repeat continuouslyA handler-created token triggers the same handler, or redelivery creates another token.Verify the self-created-client filter and durable deduplication before creating a token.
An email uses the wrong language or empty localized valuesThe handler uses a hardcoded locale or has no fallback for the selected template and localized fields.Trace the locale source and fallback sequence. Add a template and value fallback test.
A state email is stale or arrives for every transitionThe handler uses a later re-fetched state without a transition policy, or subscribes broadly without a state allowlist.Compare the Message transition with the retrieved Order and apply the documented policy and allowlist.
A return email incorrectly says a refund completedReturn-information Messages were treated as payment confirmation.Change the template to describe the return update, or trigger refund wording from an authoritative payment event.
Logs expose Customer or token dataFull envelopes, addresses, names, or template payloads are logged.Replace payload logs with correlation IDs and non-sensitive outcome fields. Redact existing retained logs according to your incident process.
Customer or Order API requests slow down or fail when email is unavailableEmail sending was coupled through an API Extension.Move the sender to an asynchronous event application.
Marketing messages or broad Customer data reach the providerThe transactional sender was reused for marketing or the Subscription and mapping include excess data.Restrict Message types and template fields. Use a separate consent-aware marketing integration for campaigns and audience synchronization.
Deployment succeeds but authorization fails at runtimeThe API Client scopes do not cover Subscription lifecycle, resource retrieval, or token creation.Compare the enabled email flows with manage_subscriptions, Customer, Order, and token requirements. Keep the final set least-privileged.