If you need to send email from Klarna after a completed checkout, the dependable pattern is not a marketplace plug-in: it is a server-side webhook flow. Klarna Checkout notifies your application about an order, your application retrieves the order from Klarna, and it then asks Volanea to deliver the email.
Klarna does not provide a native Volanea app or a generic “send an email” workflow action inside its merchant tools. That distinction matters. A payment event is valuable, but payment data and email-delivery credentials should be handled by your backend—not exposed in checkout-page code, a browser, or a public configuration object.
This guide covers the Klarna Checkout push-notification route. It is appropriate for order-confirmation follow-ups, fulfilment updates, invoice-ready messages, review requests after delivery, and internal alerts. The example uses a small Node.js/Express service, but the architecture applies equally to serverless functions, queues, and other backend stacks.
What the Klarna-to-Volanea integration actually does
The concrete trigger is a Klarna Checkout push notification. When an order is completed, Klarna sends an HTTP POST request to the push URL you supplied in the Checkout order’s merchant_urls object. The notification contains the Klarna order_id; it is a prompt for your server to fetch the authoritative order, rather than a complete, trusted email-ready order record.
The full sequence is:
- A shopper completes Klarna Checkout.
- Klarna POSTs an order identifier to your configured
merchant_urls.pushendpoint. - Your endpoint acknowledges the notification promptly.
- A background worker or handler retrieves
GET /checkout/v3/orders/{order_id}from Klarna using your Klarna API credentials. - The worker validates the order, chooses the recipient and template data, records an idempotency key, and calls Volanea’s email API.
- Volanea accepts the message for delivery and your application stores the resulting send record.
This is deliberately a two-hop design. The webhook says “there is an order to inspect”; the authenticated Klarna order-retrieval call gives your application the customer and order data it needs. Do not treat a browser redirect to your confirmation page as the trigger for transactional delivery. A customer can close the tab, lose connectivity, reload the page, or reach the confirmation URL before background payment processing has settled.
Why Klarna push notifications are the right trigger
Klarna Checkout merchant URLs serve different purposes. The checkout and confirmation URLs are customer-facing navigation URLs. They help render the checkout and confirmation experience, but they are not a reliable integration queue. The push URL is the server-to-server callback intended to tell the merchant system that it should retrieve and process an order.
A typical Checkout creation request includes merchant URLs conceptually like this:
{
"merchant_urls": {
"terms": "https://shop.example.com/terms",
"checkout": "https://shop.example.com/klarna/checkout",
"confirmation": "https://shop.example.com/klarna/confirmation?order_id={checkout.order.id}",
"push": "https://api.shop.example.com/webhooks/klarna/checkout"
}
}
The exact surrounding order-creation payload depends on your Klarna Checkout implementation and market. The important part here is the server-controlled HTTPS push URL. It should point to infrastructure you operate, not directly to Volanea and not to frontend JavaScript.
For the Checkout push flow, Klarna’s notification body is intentionally small:
{
"order_id": "c0a8010f-8a10-4b00-b613-0ec3e5357e77"
}
That limited shape is a feature, not an inconvenience. It avoids making your notification endpoint depend on a large snapshot whose state could change, and it directs you to retrieve the order with authenticated server credentials. The resulting Klarna order response can include fields such as order_id, status, purchase_currency, order_amount, merchant_reference1, billing_address, shipping_address, and order_lines, subject to the Checkout product, data supplied, and applicable privacy settings.
Do not confuse the push callback with a generic automation builder
Klarna’s Checkout push notification is not a broad no-code event bus where you select arbitrary record-created or stage-changed events. It is tied to the Checkout order lifecycle. If your use case begins elsewhere—for example, an order becomes fulfilled in an ERP, a shipment is created, or a refund is approved—make that system the event source instead.
Likewise, do not promise a message merely because the shopper sees the confirmation page. In this integration, the backend sends after it has retrieved the Klarna order and applied your own business rule. That keeps the important decision in a system where you can log, retry, deduplicate, and audit it.
Architecture: keep payment processing and email delivery separate
A safe implementation has three boundaries: the public webhook endpoint, the trusted order-processing worker, and the email provider API call. You may run all three in one small service initially, but their responsibilities should remain separate.
The webhook endpoint should do as little work as possible. Parse the order_id, reject malformed input, record the event, enqueue work, and return a successful HTTP response. Avoid waiting for order retrieval, template rendering, database work, or an email API request before replying to Klarna.
The worker performs the important operations: it fetches the order from Klarna, determines whether the event qualifies for an email, generates a stable message key, saves an outbound-message record, and sends through Volanea. This division reduces the chance that a temporary email-provider delay creates a webhook timeout.
A production-ready data model usually needs at least:
klarna_order_id: the external identifier from the push payload.email_type: for example,post_purchase_receiptorinternal_new_order_alert.recipient: the resolved email address used for the message.delivery_key: a unique value such asklarna:{order_id}:post_purchase_receipt.state: queued, sending, accepted, failed, or suppressed.volanea_message_id: the API result, if provided.- timestamps and a bounded failure reason for troubleshooting.
The database uniqueness constraint on delivery_key is more important than a process-local “already sent” variable. A process can restart, two webhook deliveries can arrive at nearly the same time, and multiple serverless instances can execute concurrently. The database is where you make a send-once decision durable.
Set up the Klarna Checkout push URL
Configure the push merchant URL during your Klarna Checkout order-creation integration. Use a publicly reachable HTTPS endpoint you control, such as:
https://api.shop.example.com/webhooks/klarna/checkout
Use separate endpoints or separate environment configuration for Klarna’s test and production environments. A test checkout should not be able to trigger a production customer email, and a production callback should not point at an endpoint that only has test credentials.
Your URL must remain stable for the lifetime of an active checkout order. Avoid deployment URLs that change per preview build. If you use a gateway, CDN, or serverless platform, ensure it forwards POST request bodies intact and does not redirect the request to a login page or a trailing-slash variant. A redirect can make a server-to-server notification fail even though it looks fine in a browser.
Respond quickly, then work asynchronously
A good webhook handler returns a 2xx status after it has safely accepted the order_id for processing. The handler should not return success before it has persisted or queued the job; otherwise a crash can lose the event. It also should not hold the HTTP request open while calling multiple external services.
The practical target is simple: acknowledge quickly, do expensive work afterward. If your architecture has a queue, insert a durable job keyed by order_id. If it does not, insert a database row in an outbox table and let a scheduled worker process it. Both choices are preferable to tying a Klarna callback directly to an outbound email request.
Retrieve the authoritative Klarna order before composing mail
The notification payload only provides the order_id. Fetch the order from Klarna Checkout before accessing customer information or amount fields. Klarna Checkout’s order-retrieval endpoint is:
GET /checkout/v3/orders/{order_id}
Klarna Checkout API credentials use HTTP Basic authentication with the credentials issued for your merchant integration. Keep those credentials in server-side secrets just as carefully as the Volanea API key. The following function demonstrates the retrieval request; it expects your secret manager to provide a Klarna username and password.
async function getKlarnaOrder(orderId) {
const basic = Buffer.from(
`${process.env.KLARNA_USERNAME}:${process.env.KLARNA_PASSWORD}`
).toString("base64");
const response = await fetch(
`https://api.klarna.com/checkout/v3/orders/${encodeURIComponent(orderId)}`,
{
headers: {
Authorization: `Basic ${basic}`,
Accept: "application/json"
}
}
);
if (!response.ok) {
throw new Error(`Klarna order retrieval failed: ${response.status}`);
}
return response.json();
}
Use the environment-appropriate Klarna API host in test versus production according to the Klarna Checkout documentation and credentials for your integration. Do not copy a production hostname and credentials into a test deployment simply because the request format is the same.
After retrieval, check the order status and your business conditions. For example, an order confirmation workflow might require a completed Checkout order and a non-empty customer email. An internal order alert might not need customer email at all; it can send to a controlled operations address and include the order reference.
Choose the recipient deliberately
Customer email may appear in the billing address, but your logic should not blindly assume every transaction has every optional field. Start with a narrow rule: send only when there is a syntactically valid email and the order has reached the state your business recognizes as complete.
For privacy, do not put unnecessary payment or customer data into email. A confirmation can state the order reference, amount formatted as currency, and a link to the customer’s account or order page. It does not need full address data, payment details, or the raw Klarna order JSON. If you collect marketing consent separately, keep promotional emails separate from transactional messages and honor the consent model that applies in the customer’s region.
Send the email through Volanea’s REST API
Volanea is the delivery layer in this design. Your backend takes the verified Klarna order fields, renders a message, and submits it over HTTPS. Before deploying, verify your sending domain and configure its authentication records so your messages align with the domain customers recognize. The email API reference and setup guides are the right place to confirm your account’s current endpoint, authentication, and message schema.
The field mapping should be explicit. In the example below:
- Klarna
billing_address.emailbecomes the Volaneatorecipient. - Klarna
merchant_reference1, falling back toorder_id, becomes the human-readable order reference. - Klarna
order_amountandpurchase_currencyare formatted as an amount. - Klarna
order_idbecomes the idempotency value stored by your application. - A verified address on your own domain becomes the sender.
Here is a complete Express-style example showing the actual push payload, authenticated order retrieval, duplicate protection, and the Volanea REST call. claimDeliveryKey represents a database insert protected by a unique constraint; it returns true only for the first worker that claims the key.
import express from "express";
const app = express();
app.use(express.json({ type: "application/json" }));
async function claimDeliveryKey(key) {
// INSERT INTO outbound_messages (delivery_key, state)
// VALUES ($1, 'sending') ON CONFLICT (delivery_key) DO NOTHING;
// Return true only if the INSERT created a row.
return true; // Replace with your database implementation.
}
async function getKlarnaOrder(orderId) {
const basic = Buffer.from(
`${process.env.KLARNA_USERNAME}:${process.env.KLARNA_PASSWORD}`
).toString("base64");
const response = await fetch(
`https://api.klarna.com/checkout/v3/orders/${encodeURIComponent(orderId)}`,
{
headers: {
Authorization: `Basic ${basic}`,
Accept: "application/json"
}
}
);
if (!response.ok) throw new Error(`Klarna returned ${response.status}`);
return response.json();
}
async function sendWithVolanea({ to, orderReference, amountText }) {
const subject = `We received your order ${orderReference}`;
const html = `
<h1>Thanks for your order</h1>
<p>Your order reference is <strong>${orderReference}</strong>.</p>
<p>Total: <strong>${amountText}</strong></p>
`;
const response = await fetch("https://api.volanea.com/v1/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VOLANEA_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify({
from: "Orders <orders@example.com>",
to: [to],
subject,
html
})
});
if (!response.ok) {
throw new Error(`Volanea send failed: ${response.status} ${await response.text()}`);
}
return response.json();
}
app.post("/webhooks/klarna/checkout", async (req, res) => {
// Klarna Checkout push payload: { "order_id": "..." }
const { order_id: orderId } = req.body ?? {};
if (typeof orderId !== "string" || orderId.length === 0) {
return res.status(400).json({ error: "Missing Klarna order_id" });
}
// In production: persist/enqueue orderId, then return 204 immediately.
res.status(204).end();
try {
const order = await getKlarnaOrder(orderId);
const recipient = order.billing_address?.email;
if (!recipient) throw new Error("Klarna order has no billing email");
const orderReference = order.merchant_reference1 || order.order_id;
const amountText = new Intl.NumberFormat("en-US", {
style: "currency",
currency: order.purchase_currency
}).format((order.order_amount || 0) / 100);
const deliveryKey = `klarna:${order.order_id}:order-confirmation`;
if (!(await claimDeliveryKey(deliveryKey))) return;
const result = await sendWithVolanea({
to: recipient,
orderReference,
amountText
});
console.log("Volanea accepted message", result);
} catch (error) {
console.error("Klarna-to-Volanea worker failed", { orderId, error });
// Mark the outbox row failed and retry from a worker; do not send blindly.
}
});
The message endpoint and fields in the example are intentionally isolated in sendWithVolanea. That makes it straightforward to update one function if you change templates, introduce tags, add reply-to handling, or adopt an account-specific API option. Do not expose that function through an unauthenticated browser endpoint.
Keep Volanea credentials out of Klarna and out of the browser
The Volanea API key does not belong in the Klarna Checkout configuration. Klarna’s merchant_urls.push configuration is a destination URL, not a secure secret store or a place to attach arbitrary Volanea credentials. The key belongs in the server environment of the middleware that receives the push callback.
Store VOLANEA_API_KEY in a managed secret store or encrypted server-side environment variable. Restrict access to the production service identity that sends mail. Rotate it on a schedule and immediately after suspected exposure. Your source repository, client bundle, mobile application, template preview configuration, browser network requests, and logs should never contain the key.
The same rule applies to Klarna API credentials. Your client should be able to load or host a checkout experience according to Klarna’s integration model, but it must not receive the Basic-auth credentials that retrieve order details. The backend is the trust boundary that can hold both the payment-platform credentials and the email-delivery key.
Add safeguards beyond secret storage
Secret storage alone does not prevent unwanted mail. Add application-level controls:
- Permit only known message types and verified sender identities.
- Validate the order ID format and retrieve the order from Klarna before using its data.
- Escape user-derived values before placing them into HTML.
- Log order IDs and message IDs, but avoid logging customer email addresses or full order objects unless your retention policy requires it.
- Use least-privilege database access for the worker and restrict who can view production logs.
An address verification step can also reduce obvious input errors when an email enters your own systems. Volanea’s free address verification tool can be useful during data-cleanup or import workflows, though it does not replace your obligation to handle customer information lawfully or to respect transactional-versus-marketing consent.
When this breaks: failures specific to the Klarna webhook hop
Webhook integrations fail in ways that ordinary synchronous API calls often do not. Build for them before launch, particularly because Klarna’s push callback and your Volanea request are separate delivery systems with separate retry behavior.
Klarna retries can create duplicate sends
A callback can be delivered more than once when your endpoint times out, returns a non-2xx response, or encounters a network interruption after Klarna sent the request. Duplicate delivery is normal behavior for webhook-style systems; it does not necessarily mean two purchases occurred.
Never use “received a webhook” as the unique send condition. Use the stable Klarna order_id plus your email type as a unique database key. If the first process has already claimed klarna:{order_id}:order-confirmation, later deliveries should safely do nothing. If a send fails before Volanea accepts it, transition the existing row to a retryable state rather than creating a fresh row that could bypass duplicate protection.
Webhook timeouts can cause a retry even after your code started work
If the endpoint retrieves the Klarna order and waits for Volanea before responding, a slow dependency can make the callback exceed the caller’s timeout. Klarna may retry while the first request is still running. That produces concurrent work and raises the likelihood of a duplicate email unless your delivery key is atomic.
Respond once work is durably queued. Then use a worker with explicit retry policies. A worker can retry a temporary DNS, network, or 5xx error with backoff; it should not automatically retry a permanent validation failure such as a missing recipient or an unverified sender domain.
Expected fields may be absent
Do not assume every payload has billing_address.email, merchant_reference1, shipping data, or the same order-line detail. Available fields can depend on the Checkout implementation, the information collected in the flow, the market, and the product configuration. A customer may also use an email that is syntactically malformed in data you imported elsewhere.
Set a clear fallback policy. For an order-confirmation email, mark the job suppressed_missing_recipient and alert the support team if email is absent. For an internal alert, send to a fixed operations inbox instead. Do not silently replace a missing customer email with a guessed address, and do not put an entire raw order payload into a message just to avoid handling a missing field.
A confirmation redirect is not evidence of successful delivery
A shopper reaching confirmation does not prove your webhook worker ran, and a successful Volanea API response does not prove the message reached an inbox. Treat these as distinct states: checkout completion, message accepted by the email API, and downstream delivery or engagement events. Persist the state you need to support customers without conflating them.
Templates, sender identity, and deliverability decisions
The first email after payment is often one of the most trusted messages a customer receives. It should come from a recognizable sender on your domain, use a clear reply path, and make the order reference easy to find. Avoid the temptation to turn a receipt-like email into a dense promotional newsletter.
A useful order message generally includes the store name, order reference, purchase total, a concise description of the next step, support contact details, and a link back to the customer’s order status where appropriate. It should not expose payment credentials, raw transaction tokens, or details a malicious recipient could use to impersonate support.
Use a template version identifier in your outbound-message record. When support asks why a customer received a particular phrase or price presentation, you can identify the exact template and data mapping used. This also helps you test a new template against test orders without changing every historical message.
Format money and locale from order data
Klarna order amounts are represented in minor units, so divide by 100 only when formatting for currencies with that convention in your implementation. A more robust production formatter should use the currency’s correct fraction digits rather than assuming two decimal places for every currency. Use the order’s purchase_currency and locale where available, and test the format for each market you support.
Keep the email language strategy explicit. If your storefront records a customer-facing locale, pass it to the template renderer. Do not infer language from country alone. A Swedish shipping address, for example, does not guarantee that Swedish is the customer’s preferred email language.
Testing the integration safely
Start with Klarna’s test environment and a Volanea test or controlled recipient setup. The goal is to prove the complete behavior, including failure handling, not merely to see a 204 response from your endpoint.
Test at least these cases:
- A normal completed test checkout produces one queued job and one accepted email request.
- The same
order_idis posted twice and results in only one customer message. - Klarna order retrieval fails temporarily, the job retries, and it still sends at most once.
- The order lacks a billing email and the workflow records a safe suppression instead of throwing repeatedly.
- Volanea returns a temporary failure, which is retried according to your policy.
- Volanea returns a permanent request error, which is surfaced to an operator with enough context to fix it.
Use a controlled destination inbox during testing. Inspect the rendered HTML, text alternative if your template supplies one, sender identity, subject, links, amount formatting, and reply-to behavior. Then test the negative paths by deliberately using an invalid Volanea key in a non-production environment and by replaying the same recorded push payload.
Do not test by manually inventing a full Klarna order object. The push body is only the order_id; your test should exercise the real order-retrieval call or a faithful mocked response. That is how you catch incorrect assumptions about optional order fields.
Alternatives when Klarna Checkout push is not your event source
If you are not using Klarna Checkout, do not force this particular endpoint into a different Klarna product. Review the notification and API capabilities of the exact Klarna product and market you use. A payment-links implementation, an order-management workflow, or a storefront platform may have a different event model and different data available.
For teams without backend engineering capacity, an automation platform can act as middleware only if it can securely receive the relevant event and call an authenticated HTTP endpoint without exposing secrets. The same core constraints remain: use a server-side or platform-managed secret, retrieve authoritative order details when the event is only an identifier, and create a durable deduplication record somewhere. A visual workflow does not eliminate webhook retries or missing fields.
A better alternative is often to trigger email from the system that owns the business milestone. Send a payment receipt from the payment/checkout workflow, a dispatch notice from the fulfilment system, and a delivery follow-up from the carrier or logistics system. That gives each message an event source that can accurately answer the customer’s implicit question: “What changed?”
Operational checklist before going live
Before production, verify the integration as an operational system rather than a code sample:
- The Checkout order creation request includes a production HTTPS
merchant_urls.pushvalue. - The public endpoint accepts the actual Klarna push body and acknowledges only after durable queueing.
- Klarna test and production credentials are isolated from each other.
- The worker retrieves the order from Klarna rather than trusting customer data from the callback.
- A database uniqueness constraint protects each order-and-message-type combination.
- The Volanea key is stored only in server-side secret management.
- The sender domain is verified and the from address is monitored.
- Failed jobs have alerts, bounded retries, and a support-visible status.
- Logs avoid raw payment and unnecessary personally identifiable information.
- Your transactional content and marketing content follow separate consent and suppression rules.
This approach may involve more engineering than pasting an API key into a browser widget, but it is the route that preserves security, observability, and reliable customer communication as order volume grows.
Conclusion
To send email from Klarna with Volanea, use the Klarna Checkout push notification as a server-side signal, not as a complete email event. Receive its order_id, retrieve the order through Klarna’s authenticated Checkout API, make an idempotent business decision, and submit the resulting message through Volanea from trusted backend infrastructure.
That design handles the realities of payment integrations: callbacks can repeat, networks can time out, order fields can be absent, and a successful API acceptance is only one part of the delivery lifecycle. With durable queueing, unique delivery keys, and protected credentials, the integration can stay reliable without a native Klarna marketplace plug-in.
FAQ
Does Klarna have a native Volanea integration?
No. Klarna Checkout can notify your server through its push merchant URL, but this is not a native Volanea app or marketplace installation. A backend service or suitable middleware connects that notification to Volanea’s email API.
What starts the email workflow in Klarna Checkout?
The trigger is the Klarna Checkout push notification. Klarna POSTs an order_id to the merchant_urls.push endpoint configured when the Checkout order is created.
Why should I retrieve the order after receiving the push notification?
The push payload is an order identifier, not a complete order record. Retrieve GET /checkout/v3/orders/{order_id} with server-side Klarna credentials to obtain and validate the data needed for your message.
Where should the Volanea API key be stored?
Store it in your backend’s secret manager or encrypted server environment. It should not be in Klarna merchant URLs, frontend code, a mobile app, a public repository, or browser-visible configuration.
How do I prevent duplicate Klarna emails?
Create a durable unique key combining the Klarna order ID and message type, such as klarna:{order_id}:order-confirmation. Claim that key atomically in your database before sending, so repeated webhook deliveries cannot create repeated emails.