SparkPost is a capable email platform with a flexible Transmissions API, strong event data, stored templates, recipient lists, and detailed sending controls. To migrate from SparkPost to Volanea safely, treat the work as an email-infrastructure migration rather than a quick API-key swap: preserve authentication, unsubscribe intent, event processing, and the sending behavior your application depends on.
The code change may be relatively contained. The operational work around it—DNS, webhook behavior, historical suppressions, template rendering, monitoring, and staged rollout—is where a migration succeeds or fails. This guide lays out a developer-first approach that lets you move deliberately, validate every assumption, and retain a rollback path until the new route is proven.
What changes when you move from SparkPost
SparkPost centers application sending around a transmission. A transmission can include one or many recipients, inline content, stored templates, recipient substitution data, metadata, campaign information, engagement-tracking controls, IP-pool routing, and more. That model is powerful because a single API request can describe a sophisticated delivery operation.
Volanea is an email API and SMTP platform for transactional and campaign email. The migration goal is not to reproduce every SparkPost object name in a new provider. It is to preserve the business behavior behind each message: who receives it, which address it comes from, what content they see, what happens after a bounce or unsubscribe, and what your product records as the message outcome.
Before touching production code, make an inventory of your SparkPost use. Split it into four groups:
- Transactional sends: password resets, magic links, receipts, invoices, account notifications, and alerts.
- Campaign sends: newsletters, product announcements, lifecycle messages, and large recipient batches.
- Operational controls: sending domains, tracking domains, bounce domains, IP pools, rate limits, and webhook subscriptions.
- Stateful data: templates, recipient lists, suppression records, engagement data, and internal mappings between your database and SparkPost message IDs.
This inventory matters because not everything has the same migration risk. A password-reset email sent from inline HTML might only require an adapter and a verified sending domain. A campaign pipeline using stored recipient lists, SparkPost substitution language, A/B testing, and list-specific suppression rules needs a more deliberate redesign.
Keep the migration boundary in your application
The best technical preparation is to isolate your provider-specific email code behind an internal interface. Instead of letting application features import the SparkPost client directly, give them a stable function such as sendEmail() or sendTransactionalEmail().
That interface should express your product’s needs rather than a vendor’s schema:
export type OutboundEmail = {
from: string;
to: string[];
subject: string;
html?: string;
text?: string;
replyTo?: string;
tags?: string[];
metadata?: Record<string, string>;
idempotencyKey?: string;
};
export type SendResult = {
providerMessageId: string;
acceptedAt: string;
};
export interface EmailProvider {
send(message: OutboundEmail): Promise<SendResult>;
}
Once your product speaks this internal shape, SparkPost and Volanea become implementations rather than assumptions spread through checkout flows, authentication handlers, background jobs, and admin tools. That reduces migration risk now and makes a future provider change far less expensive.
Audit your current SparkPost implementation first
A migration plan should start with evidence, not memory. Search your source code, environment configuration, deployment secrets, and operational runbooks for SparkPost references. Include the obvious items such as sparkpost, SPARKPOST_API_KEY, and api.sparkpost.com, but also look for old helpers and queue workers that may only run monthly or during a billing event.
Build a concise migration worksheet containing the following information for every sending workflow:
| Area | Questions to answer before cutover |
|---|---|
| Sender identity | Which From, Reply-To, Return-Path, and tracking domains does it use? |
| Message type | Is it transactional, campaign, or mixed? |
| Content | Is it inline HTML/text, a stored template, raw MIME, or an A/B test? |
| Personalization | Does it use recipient substitution data, global substitution data, or metadata? |
| Audience | Does your app supply recipients, or does SparkPost supply a stored recipient list? |
| Tracking | Are click/open tracking, a tracking domain, or UTM additions expected? |
| Deliverability controls | Does it require an IP pool, a subaccount, a sending domain, or traffic shaping? |
| Events | Which webhook event types change application state? |
| Suppressions | Are suppressions account-wide, transactional, non-transactional, or list-specific? |
| Reporting | Which SparkPost metrics and dashboard reports feed internal decisions? |
Do not regard this as paperwork. It determines what you need to test. For example, a receipt sender that only records “accepted by provider” has different requirements from a login system that invalidates an email address after a permanent bounce. Likewise, a marketing sender that relies on campaign IDs for reporting needs a replacement tagging convention before the first Volanea campaign is launched.
Identify SparkPost-only assumptions
SparkPost’s Transmissions API can use inline content, a stored template, an A/B test, or raw RFC822 content. It also supports options and fields such as transactional, inline_css, ip_pool, campaign_id, recipient lists, substitutions, and overrides. Those are useful capabilities, but they should be treated as explicit compatibility questions—not silently assumed to map field for field.
Mark every SparkPost-specific behavior in your audit. Common examples include:
content.template_idand provider-hosted template versions.- SparkPost substitution syntax and conditional rendering.
substitution_dataat global and recipient level.- Stored recipient lists and recipient-list IDs.
campaign_idreporting conventions.ip_poolselection and warm-up procedures.inline_cssrendering behavior.- Raw RFC822 submissions.
- Transmission-level A/B tests.
- Account-wide versus
list_id-scoped suppression handling. - SparkPost event names, event payload fields, and webhook batch semantics.
The right outcome may be a direct replacement, an application-owned equivalent, or a decision to stop using a capability. The important part is making that choice consciously.
Before and after: replace the SparkPost SDK call
The sending request is usually the most visible code change. The following example shows a typical SparkPost Node.js SDK transmission call for a single transactional message with recipient personalization and application metadata.
Before: SparkPost SDK transmission
import SparkPost from 'sparkpost';
const sparkpost = new SparkPost(process.env.SPARKPOST_API_KEY!);
const result = await sparkpost.transmissions.send({
options: {
transactional: true,
},
content: {
from: 'Acme <receipts@notify.example.com>',
subject: 'Your receipt for {{total}}',
html: '<p>Hi {{first_name}},</p><p>Your total is {{total}}.</p>',
text: 'Hi {{first_name}}, your total is {{total}}.',
},
recipients: [
{
address: {
email: 'ada@example.net',
name: 'Ada Lovelace',
},
substitution_data: {
first_name: 'Ada',
total: '$49.00',
},
metadata: {
order_id: 'ord_123',
},
},
],
campaign_id: 'receipt',
});
console.log(result.results.id);
This represents the SparkPost pattern accurately: instantiate the SDK with an API key, then call transmissions.send() with content, recipients, and optional transmission controls. SparkPost can process a single recipient or a batch in one transmission, and its API supports recipient-specific metadata and substitution data.
After: application-owned Volanea send call
The safest migration pattern is to replace direct vendor calls with your internal provider adapter. This example is intentionally explicit about the boundary: the application creates a normalized message, and the Volanea adapter performs the provider-specific request using the current API contract in the Volanea API reference and setup guides.
// email/volanea-provider.ts
import type { EmailProvider, OutboundEmail, SendResult } from './provider';
export class VolaneaProvider implements EmailProvider {
async send(message: OutboundEmail): Promise<SendResult> {
// Use the current Volanea REST endpoint, authentication header,
// and request schema from /docs here. Keep those details inside
// this adapter rather than throughout your application.
const response = await sendWithVolaneaApi({
from: message.from,
to: message.to,
subject: message.subject,
html: message.html,
text: message.text,
replyTo: message.replyTo,
tags: message.tags,
metadata: message.metadata,
idempotencyKey: message.idempotencyKey,
});
return {
providerMessageId: response.id,
acceptedAt: response.createdAt,
};
}
}
// email/send-receipt.ts
const emailProvider = new VolaneaProvider();
await emailProvider.send({
from: 'Acme <receipts@notify.example.com>',
to: ['ada@example.net'],
subject: 'Your receipt for $49.00',
html: '<p>Hi Ada,</p><p>Your total is $49.00.</p>',
text: 'Hi Ada, your total is $49.00.',
tags: ['receipt'],
metadata: {
order_id: 'ord_123',
},
idempotencyKey: 'receipt:ord_123',
});
There are two deliberate differences in the after example. First, personalization is rendered in your application before the send. This avoids assuming SparkPost template tokens will work unchanged elsewhere. Second, campaign_id is converted to a portable tag such as receipt, while the durable business identifier remains in metadata.
Do not copy an unverified REST URL, authentication header, field name, or SDK method from a migration article into production. Provider API syntax changes over time, and the exact Volanea request should come from the current documentation. Keeping that request inside sendWithVolaneaApi() means one verified implementation serves every mail flow.
Why render simple transactional content in your app
For transactional email, application-side rendering is often simpler to operate than making provider templates the only source of truth. Your Git history contains the template change, tests can render expected content locally, and a deployment can update code and content together.
That does not mean all teams should abandon provider templates. A provider-hosted template workflow can be appropriate when non-engineering teams need controlled content edits, when template publishing is governed separately from application deployments, or when campaigns are assembled outside the app. The migration decision is architectural: decide where content lives, then migrate each message class accordingly.
Rebuild domain authentication without breaking existing mail
Your From domain is central to deliverability and brand trust. It is also the part of an email migration that should not be rushed. SparkPost sending domains can have DKIM, CNAME ownership verification, tracking-domain configuration, and bounce-domain behavior. The exact DNS records Volanea asks you to publish will be specific to the domain configuration it generates.
Do not delete SparkPost DNS records simply because Volanea verification is complete. During a staged migration, both providers may need valid authentication records. The safe process is to add Volanea’s required records, confirm they resolve publicly, verify the domain in Volanea, send test messages, and only remove retired provider records after all SparkPost traffic has stopped and the rollback window has closed.
DNS and authentication checklist
Re-verify every one of these items for each sender domain and subdomain:
- From-domain ownership: Add the exact DNS record Volanea provides for domain verification and wait for its verified status.
- SPF: Ensure there is only one SPF TXT record at a given hostname. Merge all authorized sending services into that single record rather than publishing multiple
v=spf1records. - DKIM: Publish the exact selector and public-key record Volanea supplies. Confirm a real message includes a passing DKIM signature aligned with the visible From domain.
- DMARC alignment: Check that either SPF or DKIM aligns with the organizational domain used in the From header. Review the policy before changing sender infrastructure.
- Return-Path or bounce domain: If you used a SparkPost bounce domain, configure the corresponding Volanea sending or bounce identity as documented and inspect the delivered message headers.
- Tracking domain: If click tracking is enabled, configure and verify the Volanea tracking domain before moving any messages that contain tracked links.
- Subdomains: Verify each actual sender independently—such as
notify.example.com,mail.example.com, andnews.example.com—rather than assuming the apex domain covers all workflows. - DNS TTL and propagation: Lower TTLs before a planned change only when appropriate, but allow for resolver caching and validate from more than one network.
SPF deserves special attention. An SPF record is not a list of DNS records that can safely coexist. Publishing a second SPF TXT record can cause SPF evaluation to fail. Consolidate authorized senders into one policy and keep DNS lookup limits in mind as you add providers.
Use a real seed inbox to inspect the final headers after each test send. Look for spf=pass, dkim=pass, and dmarc=pass; verify that the domain shown in the DKIM signature and envelope sender align with your intended From identity. DNS verification in a dashboard is necessary, but a received message is the more meaningful end-to-end check.
Migrate webhooks as a data-contract change
Email event processing is where an apparently successful migration can quietly create product bugs. Your application may use SparkPost events to mark a message delivered, stop sending to a hard-bounced address, record an unsubscribe, trigger support tooling, or calculate campaign metrics. That behavior must be recreated from Volanea’s event model rather than merely pointing a new provider at the old URL.
SparkPost webhooks deliver batches of raw events through POST requests. SparkPost documents a 10-second endpoint timeout and retries for webhook batches that do not receive HTTP 200, with retry activity lasting up to eight hours. It also provides a batch ID header and unique event IDs to support duplicate prevention. Your existing webhook handler may have been built around those behaviors.
Treat Volanea webhooks as a new contract. Verify the current event types, signature or authentication method, delivery retry behavior, payload format, and retry headers from the Volanea documentation. Then make your endpoint tolerant of both providers during the cutover period.
Build a provider-neutral event pipeline
A durable design separates receipt from interpretation:
- Receive the webhook and authenticate it according to the provider’s documented method.
- Store the unmodified payload, provider name, receipt time, and delivery headers in durable storage.
- Return success quickly after durable acceptance.
- Process the event asynchronously.
- Deduplicate on a provider event ID when available; otherwise use a carefully designed provider/message/event composite key.
- Convert the provider event into an internal event such as
delivered,hard_bounce,complained,unsubscribed, ordeferred. - Apply business actions only from the internal event.
This architecture avoids coupling your customer database to a particular JSON payload. It also helps with replaying events during a debugging incident: you can reprocess stored provider events without asking a provider to resend them.
Event mapping questions to answer
Build a mapping table before enabling the Volanea webhook in production:
| Business meaning | Existing SparkPost signal | Volanea equivalent to verify | Application action |
|---|---|---|---|
| Provider accepted a send | Transmission response or injection event | Send response and/or accepted event | Store provider ID and accepted timestamp |
| Delivered | Delivery event | Delivery event | Mark message delivered; do not imply read |
| Permanent failure | Bounce event and bounce classification | Permanent-bounce event and classification | Suppress or flag address under your policy |
| Temporary failure | Delay or transient failure event | Deferred/retry event | Observe, alert when persistent, do not immediately suppress |
| User opt-out | Unsubscribe event | Unsubscribe event | Record consent withdrawal and suppress future relevant mail |
| Spam complaint | Spam complaint event | Complaint event | Suppress promptly and investigate source flow |
| Engagement | Open/click events | Tracking events, if enabled | Use carefully; do not treat as reliable proof of user behavior |
Do not automatically equate “accepted” with “delivered.” Accepted means the email provider took responsibility for processing the request; it does not guarantee that a recipient server accepted the message. Similarly, an open pixel may be affected by image proxying, privacy protections, or clients that block images. Use those signals as operational metrics, not as absolute facts about an individual recipient.
Export and protect your suppression list
Suppression migration is both a compliance concern and a deliverability concern. SparkPost’s suppression list supports transactional and non-transactional suppression types, with account-wide or list-specific scope. A historical record may represent an unsubscribe, a hard bounce, a complaint, or a more granular list-specific preference.
Your Volanea account should not become a blank slate that sends again to addresses your system previously learned not to contact. Export the SparkPost suppression data before cutover, preserve its reason and scope where available, normalize email addresses consistently, and import records using Volanea’s supported workflow.
A safe suppression import process
Use this sequence instead of a one-time spreadsheet upload with no audit trail:
- Export a snapshot. Capture the SparkPost suppression list immediately before the initial import and record the export time, record count, and source account or subaccount.
- Keep source fields. Retain the original email, type, reason, scope, list identifier, creation time, and any available description in a secure migration file.
- Normalize carefully. Trim whitespace, lowercase the domain portion, reject malformed addresses, and avoid aggressive transformations that may collapse distinct local parts.
- Classify by policy. Separate permanent delivery failures, complaints, marketing opt-outs, transactional opt-outs, and list-specific opt-outs. Your legal and product policy should govern how each category is handled.
- Import conservatively. If Volanea’s suppression model does not preserve every SparkPost scope, choose the safer behavior rather than risking an unwanted send.
- Reconcile counts. Compare exported, accepted, rejected, and imported totals. Investigate differences rather than ignoring them.
- Run a delta import. New bounces, complaints, and unsubscribes can occur while migration work is underway. Import changes from the first snapshot through final cutover.
- Keep both systems updating during overlap. Until SparkPost is fully retired, continue processing its suppression-relevant events and synchronize them into your internal suppression source of truth.
For future resilience, maintain a first-party suppression ledger in your own database. The provider’s list remains important for enforcement at send time, but your product should retain the customer’s consent or delivery history independently. This makes provider migrations, audits, and cross-channel preference management easier.
If you need to clean a recipient upload before a campaign, use an address validation step rather than discovering invalid recipients through a full send. Volanea’s email address verification tool can be useful for checking individual addresses, but it should complement—not replace—your historical bounce and unsubscribe records.
Plan for templates and personalization differences
Template migration is often harder than the API migration. SparkPost supports stored templates, published versions, substitution data, recipient-level variables, and transmission-level global substitution data. Its template language and rendering rules may be embedded in emails that have evolved over years.
Do not assume a template copied as HTML will produce the same output somewhere else. Differences can appear in variable delimiters, escaping, conditional blocks, loops, missing-value behavior, subject rendering, CSS inlining, click-tracking rewrites, and the handling of plain-text alternatives.
Choose one of three template paths
Path one: render transactional templates in your application. This is usually the least ambiguous migration option for engineering-owned messages. Convert each template to a component or server-side rendering function, validate its input schema, render HTML and text, and send the finished content through Volanea.
Path two: recreate templates in Volanea. Choose this when the current Volanea feature set and documented syntax meet your requirements, and when provider-managed templates are important to your workflow. Rebuild and test each template rather than attempting a mechanical token replacement.
Path three: keep campaigns in a dedicated campaign workflow. For large broadcasts or complex marketing automations, preserve an explicit audience, consent, segmentation, and scheduling model. Do not treat campaign email as a loop over transactional API calls unless that is a deliberate, compliant design.
Create a template test matrix
For every migrated template, render at least these cases:
- Typical customer data with all expected fields.
- Missing optional fields.
- Names with apostrophes, non-Latin characters, and HTML-sensitive characters.
- Long product names and unusually large invoice totals.
- URLs with query strings and signed tokens.
- A recipient who has opted out of campaign mail.
- Mobile and desktop rendering in representative clients.
- Text-only rendering and accessibility checks.
Snapshot tests are useful here. Save the expected HTML and text output for known inputs, then compare after each template conversion. Also send real samples to Gmail, Outlook, Apple Mail, and at least one privacy-focused client before broad rollout.
Understand what may not map one to one
A factual migration guide should acknowledge that providers are not interchangeable at every layer. SparkPost may remain the better fit for teams that rely deeply on specific transmission features, existing template investments, sophisticated provider-hosted recipient management, or established reporting and sending controls.
Volanea may be a better fit when you want a simpler email API or SMTP integration, an application-owned email layer, and a fresh operational setup for transactional and campaign sending. The right choice depends on the workflow, volume, governance model, and team ownership—not a blanket claim that one platform is universally better.
Here are SparkPost capabilities that deserve explicit redesign or verification during a move:
- Transmission batches: SparkPost can address many recipients in one transmission. Decide whether your Volanea workflow uses a documented batch capability, an application queue, or campaign tooling.
- Stored recipient lists: If SparkPost stores audience data, export it with consent and metadata. Re-establish segmentation in your own database or the appropriate destination.
- Substitution data: Convert provider-specific variables into typed application data or documented Volanea template variables.
- A/B tests: Recreate experimentation logic outside the provider if no direct equivalent is documented.
- IP pools: Do not assume a pool name, dedicated-IP setup, or warm-up posture transfers. Discuss sending volume and reputation requirements before shifting material traffic.
- Raw RFC822 messages: Verify whether your new workflow supports the same raw-MIME needs, especially for signed, forwarded, or attachment-heavy messages.
- Inline CSS: If SparkPost previously inlined CSS for you, add a rendering/inlining step or verify equivalent behavior before email clients expose broken layouts.
- Campaign reporting: Preserve business tags, message categories, and internal IDs so reporting continuity does not depend on matching SparkPost dashboard fields.
- Subaccounts and permissions: Map account boundaries, key rotation, team access, and tenant isolation as part of the security review.
Run a staged cutover instead of a big-bang switch
The safest migration is progressive. Begin with a narrow, observable class of mail and expand only when evidence supports it. This reduces risk to authentication and lifecycle email, which are usually more damaging to get wrong than promotional sends.
A practical rollout sequence looks like this:
- Configure Volanea and verify all required sending identities.
- Implement the Volanea adapter, response logging, and webhook receiver.
- Import an initial suppression snapshot.
- Send non-production test messages and inspect headers, rendering, links, and events.
- Move an internal-only or low-volume transactional flow.
- Compare acceptance, delivery, bounce, complaint, and support signals with the existing route.
- Move additional transactional flows by message category.
- Import a suppression delta, then move campaign traffic only after its template, consent, and audience workflow is ready.
- Keep SparkPost credentials and DNS active only for the defined rollback window.
- Retire old credentials and records after no production dependency remains.
Use deterministic routing during the overlap
Avoid random switching that makes debugging difficult. Route by a stable rule such as message type, environment, tenant allowlist, or a hash of an internal message ID. Log the selected provider with every send attempt.
For example, start with employee notifications, then 1% of a non-critical receipt type, then a named tenant cohort. If you need to compare providers for the same workflow, do not send duplicate customer emails. Compare operational metrics across distinct cohorts or test recipients instead.
Define rollback conditions before launch
A rollback plan is only useful if it has concrete triggers. Agree on thresholds appropriate to your baseline volume and message type, such as an unexpected rise in provider errors, authentication failures in received headers, a webhook processing backlog, missing unsubscribe enforcement, material changes in hard-bounce rate, or a sudden increase in support tickets about missing messages.
Your rollback should be a configuration change, not an emergency code rewrite. That is another benefit of an internal EmailProvider adapter and feature-flagged routing.
Test deliverability and observability end to end
A successful API response is the beginning of validation, not the finish line. Test the full path from queue job to recipient inbox and back to your event store.
For each message category, verify:
- The correct From, Reply-To, and display name.
- Passing SPF, DKIM, and DMARC in the delivered message headers.
- Correct Return-Path or bounce identity where relevant.
- A working unsubscribe mechanism for campaign mail.
- Correct link destinations before and after tracking rewriting.
- The expected HTML, text, attachments, and locale formatting.
- Provider message IDs stored against your internal message record.
- Event delivery to your webhook endpoint.
- Idempotent handling of webhook retries and duplicate events.
- Suppressed recipients are not sent mail.
- Permanent bounces and complaints update the internal suppression ledger.
Monitor both technical and outcome metrics. Technical metrics include send latency, queue depth, API errors, webhook response time, and retry counts. Outcome metrics include accepted, delivered, deferred, bounced, complained, unsubscribed, and support-reported missing-message rates. Compare like with like: a password-reset message and a campaign message have very different expected behavior.
The parts that are genuinely harder to migrate
Some migration tasks take more time because they involve historical state or hidden business logic rather than code syntax.
Large historical suppression lists are difficult because they may include mixed reasons, duplicate addresses, multiple scopes, legacy list IDs, and records from past acquisitions or old products. The safest import behavior may be more conservative than your existing implementation, particularly where list-specific preferences cannot be represented exactly.
Template syntax differences are difficult because templates contain more than variables. They encode formatting decisions, escaping behavior, conditional logic, fallback values, localization, and sometimes business rules that should never have lived in email markup. Plan a conversion and test phase; do not promise a token-for-token migration.
Provider-generated reporting history usually does not move. Export the reports and event data you need for finance, customer support, compliance, or trend analysis before retention windows close. Keep historical reporting separate from the operational migration goal.
Reputation and sending posture also require care. A new route can change the sending infrastructure recipients observe, even when the From domain remains the same. Keep volume growth controlled, preserve list hygiene, and avoid using the migration as a reason to send to old or uncertain contacts.
Webhook semantics can create delayed failures. An event name may sound comparable while carrying a different classification, timestamp, or retry model. Normalize events at the edge and test your business actions with recorded payload fixtures.
Conclusion: move the system, not just the request
To migrate from SparkPost to Volanea successfully, start with an inventory, create a provider boundary in code, re-authenticate every sending identity, protect suppression data, and treat webhooks and templates as deliberate migrations. The send call is important, but it is only one part of an email system that includes consent, identity, reputation, reporting, and customer experience.
Keep SparkPost running long enough to preserve a clean rollback option, migrate in measured cohorts, and make decisions based on observed delivery and event data. By keeping your application’s email model independent of either provider, you reduce immediate migration risk and gain a more maintainable sending architecture for the next stage of your product.
FAQ
How long does it take to migrate from SparkPost to Volanea?
A simple transactional integration can be prepared quickly once domains are verified and the Volanea API contract is implemented. Production migration time depends more on DNS propagation, templates, webhook testing, suppression imports, and staged traffic validation than on replacing the SDK call.
Can I keep my SparkPost domain records during the migration?
Yes. In fact, keeping the existing SparkPost configuration active during a defined overlap period is often safer. Add and verify Volanea’s required records first, test both routes, and remove retired records only after the SparkPost rollback window is closed.
Do SparkPost templates migrate directly to Volanea?
Do not assume so. SparkPost template syntax, substitution data, rendering behavior, and stored-template workflow may not map directly. Recreate templates using application-side rendering or Volanea’s documented template features, then test output with representative data.
Should I import every SparkPost suppression record?
Import historical suppressions conservatively, especially unsubscribes, complaints, and permanent delivery failures. Preserve reason and scope in your own migration records, run a delta import near cutover, and continue processing suppression-relevant events during the provider overlap.
Can I switch all mail at once?
You can, but a staged cutover is lower risk. Move one message category or cohort at a time, monitor authentication and event behavior, and keep a configuration-based rollback path until delivery and suppression handling are proven.