Transactional Email on SAP Commerce Cloud: Providers, Protocols, and Deliverability
CCv2 ships no SMTP relay, so email is your architecture decision: SMTP versus provider REST APIs, a custom EmailSendStrategy, SPF/DKIM/DMARC setup, bounce handling, and the testing regime that keeps order confirmations out of spam.
Order confirmations, password resets, registration emails: transactional mail is the one integration every commerce project has, and on CCv2 it arrives with a surprise. The legacy SAP-managed infrastructure provided an SMTP relay per environment; the public cloud platform provides none. Email is your problem, end to end: provider, protocol, sender reputation, bounce handling. Teams that discover this in week two make a calm architecture decision; teams that discover it during UAT ("why has nobody received a single order confirmation?") make a panicked one. This guide is the calm version.
How Commerce Generates Email#
Two platform subsystems cooperate:
- WCMS renders the content. Email pages and templates are CMS objects: pages, templates, and components rendered to HTML with order data, customer data, and localized content. Business users edit them like storefront content. This remains true in headless projects: even with a composable storefront, the platform's business processes render and send the OOTB emails unless you deliberately re-platform them.
- The business process engine sends it. Order placed, registration completed, password reset: each triggers a process (defined in process-definition XML) whose steps include email generation and send actions. The send itself goes through
DefaultEmailServiceinacceleratorservices, down to the platform'sMailUtils, which wraps Apache Commons Email over the Java Mail API.
Knowing the machinery matters for operations: a "customer got no email" ticket decomposes into did the process run (Backoffice, business process logs), did rendering succeed (an EmailMessage exists), and did the send succeed (the EmailMessage is cleaned up automatically only when sent successfully; failed sends accumulate, which makes them both your retry queue and, unmonitored, your database growth problem, as covered in the data maintenance guide).
The Provider Decision#
With no platform relay, two viable paths:
| Self-hosted SMTP relay | Email service provider (SaaS) | |
|---|---|---|
| Setup | You install, configure, secure, monitor | Account, DNS records, credentials |
| Reputation management | Entirely yours: IP warm-up, blocklist monitoring | Provider's shared or dedicated IPs, managed |
| Bounce/complaint handling | Build it | Webhooks/APIs out of the box |
| Analytics | Build it | Dashboards, event streams |
| Sensible when | Corporate mail infrastructure already exists with staff who run it | Almost everyone else |
For most CCv2 projects the SaaS provider is the correct default: deliverability is a specialized operations discipline (reputation, feedback loops, ISP relationships), and buying it costs less than one deliverability incident. Outbound traffic from CCv2 is unrestricted, so no allowlisting is needed to reach any provider.
Protocol: SMTP API or REST API#
Most providers expose both an SMTP interface and a REST API. The trade-off decides how much you build.
SMTP: zero code, four properties#
The shipped DefaultEmailService speaks only SMTP (typically port 587, sometimes 443 for wrapped setups). Point it at the provider:
mail.smtp.server=smtp.provider.example
mail.smtp.port=587
mail.smtp.user=apikey
mail.smtp.password=<provider-credential>
mail.from=orders@shop.acme.com
mail.replyto=support@acme.com
The credential is a secret: it belongs in the Cloud Portal service configuration per environment, never in the repository (see the first-deployment guide for the mechanics). With this, every OOTB email flows unchanged, which is why SMTP is the right starting point for nearly every project: you get to go-live on four properties and revisit later if you need provider features.
REST API: a custom EmailSendStrategy#
Provider REST APIs offer what SMTP cannot: template management on the provider side, message tagging, event callbacks correlated by message id, batch sending. Integrating one means a small extension that swaps the send step while keeping all the WCMS rendering and process machinery:
public class ProviderApiEmailSendStrategy implements EmailSendStrategy {
private final ProviderClient client; // most ESPs ship a Java SDK
@Override
public void send(final EmailMessageModel message) {
final SendRequest request = SendRequest.builder()
.from(message.getFromAddress().getEmailAddress())
.to(toRecipients(message.getToAddresses()))
.subject(message.getSubject())
.html(message.getBody())
.tag("commerce-" + message.getProcess()) // correlate provider events back
.build();
final SendResult result = client.send(request);
message.setSent(result.isAccepted());
// persist the provider message id for bounce correlation
}
}
wired over the default via the usual alias override in your extension's Spring config. The strategy seam means the rest of the email pipeline (templates, processes, retries) does not know or care that SMTP left the building. Budget a few days including tests; the provider SDK does the heavy lifting.
Choose REST when you concretely need provider-side events tied to messages, or when the provider's SMTP endpoint is throttled below your peak (order confirmation bursts during a sale are exactly when you cannot afford queuing).
Deliverability: The Part That Decides Whether Anyone Reads Your Mail#
Sending is easy; landing in the inbox is engineering. Whatever provider you choose, this is the non-negotiable DNS and process work, illustrated here with the Amazon SES setup pattern but identical in shape everywhere:
- Verify the sender domain. Prove domain ownership to the provider (DNS TXT record). Send from a subdomain you can sacrifice reputationally if needed (
mail.acme.comorshop.acme.com), not the corporate apex. - SPF: authorize the provider's servers to send for your domain:
v=spf1 include:amazonses.com ~all
- DKIM: cryptographic signing, keys published as CNAME/TXT records the provider generates. Every serious provider automates this; your job is getting the records into DNS and verifying they resolve.
- DMARC: the policy tying SPF and DKIM together, plus the reporting address that tells you who is spoofing you:
_dmarc.acme.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@acme.com"
Start at p=none to observe, move to quarantine/reject once reports are clean. Mail without DMARC alignment increasingly lands in spam at major ISPs regardless of content.
- Bounce and complaint feedback. Hard bounces and spam complaints must flow back and stop future sends to those addresses; ignoring them poisons your sender reputation mechanically. The SES reference architecture is representative: SES publishes bounce/complaint events to an SNS topic, SNS feeds an SQS queue, and a consumer job pulls and processes them. On commerce, that consumer is a cronjob (or a small side-by-side function) that flags the customer record and suppresses sends. One warning from that architecture that generalizes: queues are consumed destructively, so never point two environments at the same queue. Stage and production each get their own provider configuration, or stage silently eats production's bounce events.
- Escape the sandbox before go-live. Providers start new accounts in a restricted mode (SES famously: verified recipients only, tiny quotas). The production-access request takes days and asks about volumes and bounce handling. File it in the go-live runbook two weeks out, not on cutover day.
- Warm up honestly. A brand-new domain/IP jumping to 100k sends on day one is what spam looks like to ISPs. Ramp volume over days to weeks; providers document their curves.
Testing Without Emailing Real Customers#
- Local: point
mail.smtp.serverat a capture tool (MailHog, smtp4dev, Mailpit). Every developer sees rendered emails in a web UI, nothing leaves the machine, and template work gets a real preview loop. - Dev/stage: two rules that must both hold. First, non-production environments never use the production provider credentials (reputation and quota isolation, plus the queue-crossing hazard above). Second, environments restored from production contain real customer email addresses, so stage must either send only to an allowlist, redirect all mail to a test inbox (an interceptor on the send strategy is a ten-line customization), or run against a capture service. A stage environment that can email real customers will eventually email real customers.
- Pre-go-live: an end-to-end pass per email type per language: trigger the real process, receive at real mailboxes across major ISPs (Gmail, Outlook at minimum), check inbox placement rather than mere delivery, and validate rendering in the clients your customers actually use. Then verify the failure path once: break the provider credential on stage and confirm the process parks the
EmailMessagefor retry instead of losing it.
Decision Summary#
| Situation | Do this |
|---|---|
| Standard project, standard emails | SaaS provider over SMTP; four properties; spend the saved time on the DNS checklist |
| Provider events, tagging, or high burst volume needed | Same provider over REST via a custom EmailSendStrategy |
| Corporate mandate for internal mail infra | Self-hosted relay, with reputation ops explicitly staffed |
| Marketing mail | Not this pipeline. Campaigns belong in a marketing tool (Emarsys or equivalent); commerce sends transactional mail only, which also keeps your transactional reputation insulated from campaign behaviour |
The pattern of the whole topic: the code integration is a day, the deliverability engineering is a checklist, and the incidents all come from skipping the checklist. Wire the four DNS records, own your bounces, rehearse the sandbox exit, and transactional email becomes what it should be: boring.