Skip to content

Callbacks and Outbound Messages

Module 7 of 8 in the chapter Salesforce as an API Consumer, chapter 9 of the Integration Architect course. Each module stands on its own. Previous: AI in the Work · Next: Testing Callouts

The question this module answers: How do outbound messages push a change out of Salesforce, what can’t they promise, and how does the other system call back for the rest?

Every module so far has been Salesforce making the call itself. Outbound messages are different: Salesforce makes the call for you, from its own queue, to a listener someone else builds. It’s the “pushes the old way” set on the map. It’s an old car that’s still running. You probably won’t build a new one, but you’ll find them in older orgs, and they’re still on the exam.

A record changes, and a flow sends an outbound message. Since Workflow Rules ended, it’s always a flow that triggers it. Salesforce doesn’t call your system right then. It puts a notification in its own outbound message queue, and a background process does the sending (Outbound Messaging (opens in a new tab), SOAP API Developer Guide, checked 2026-10-31).

Here’s the twist. Normally the server gives out the WSDL and the client uses it. Here, Salesforce is the client, but it gives you the WSDL and says: go implement this as a server. You build the listener, in .NET, in Java, or in an integration platform like MuleSoft, to Salesforce’s contract. When the message arrives, your listener has to send back an acknowledgement. Think of a letter that needs a signed receipt: until the mail carrier gets the signature, they keep coming back to deliver it again.

What it costs to build: almost nothing in Salesforce. A flow and an outbound message, no code. That’s why it was popular, and why it’s still running in so many orgs.

Salesforce is honest about its limits (Understanding Notifications (opens in a new tab), checked 2026-10-31):

  • Up to 100 notifications in one message. Your listener has to handle one, or a hundred.
  • 24 hours, then gone. If your listener is down, messages wait in the queue and are retried, with the gap growing up to two hours, until they’re 24 hours old. Then they’re dropped.
  • Out of order. Each message is retried on its own, so a later change can arrive before an earlier one.
  • Sometimes twice. “While each message is usually delivered one time, it can sometimes be delivered more than one time.” So the listener has to handle duplicates, the same lesson as receiving a webhook in chapter 8.
  • Not an audit trail. In Salesforce’s words: “You can’t build an audit trail using outbound messaging.”
  • Only the latest data. If the record changes again before the message is sent, only the latest values go. The changes in between are never seen.

It’s kind of guaranteed delivery, but not. You can watch the queue in Setup, retry a stuck message, or delete one, but you can’t query it from code.

An outbound message carries fields from the record that changed, and you can reach up to its parent, but not down to its children. Close an opportunity, and the message can carry the opportunity, but not its products. For a delivery, it can carry the delivery, but not its items.

So when the other system needs more, it calls back. The message says “this changed”; the listener turns around and calls into Salesforce, through one of the doors from chapter 8, to fetch the children. That’s the “notice with the ID” shape from chapter 7, and it works the same for a platform event or a webhook: send the notice, let the receiver ask for what it needs.

An outbound message, and the callback An outbound message, and the callback. In Steve Trucking's org, a delivery changes and a flow sends the outbound message, which queues in the outbound message queue, in amber and tagged the old way: up to 100 per message, retried for 24 hours. The queue delivers it by SOAP to your listener on the other side, built to Salesforce's WSDL, which must acknowledge it. The listener then calls back with its own OAuth token, through a door in, the REST API, where it sees only what its integration user may see, and reads the delivery's items, the children the message couldn't carry. The legend: amber is legacy, still running, not for new work. Underneath: no session ID travels in the message since February 2026; the callback signs in for itself. {"author": "Steve Simpson", "blueprint": "1.0.0", "builder": "standard 1.57.1", "built": "2026-10-31", "id": "DGM-INT-SC-06", "rights": "\u00a9 2026 Twinshark LLC. All rights reserved.", "template": "cells 0.5.0", "theme": "stevetecharc 1.4.0"} IN STEVE TRUCKING'S ORG THE OTHER SIDE A delivery changes a flow sends the outbound message The outbound message queue up to 100 per message; retried for 24 hours the old way Your listener built to Salesforce's WSDL; must acknowledge A door in: the REST API only what the listener's integration user may see The delivery's items the children the message couldn't carry queues delivers (SOAP) calls back, own OAuth token reads No session ID travels in the message since February 2026. The callback signs in for itself. HOW TO READ IT legacy: still running, not for new work © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

The message says what changed. The callback fetches the rest, with its own key.

For years, the outbound message could carry a session ID: a ready-made key the listener could use to call straight back in. That was convenient, and that was the problem. A session key travelling inside a message is a key someone else could pick up.

So Salesforce removed it. “You can no longer send session IDs in outbound messages starting February 23, 2026. Instead, use OAuth to authenticate.” The checkbox is gone, the API setting is ignored, and the session ID element in the message is empty (Sending Session IDs in Outbound Messages Is Being Removed (opens in a new tab), Spring ‘26 release notes, checked 2026-10-31). If you find an old listener that called back with the session ID, it stopped working that week.

The callback today works like any other call into Salesforce: the listener has its own External Client App and its own integration user, and gets its own token, with the client credentials flow from chapter 4. It’s a call-in like any other, and the integration user decides what it can see.

Your listener is a public address, so check who’s calling. Salesforce’s advice: accept requests only from Salesforce’s IP ranges, and use TLS so nothing can be read on the way (Considerations for Security (opens in a new tab), checked 2026-10-31). But read their own warning: the IP range “guarantees that the message came from Salesforce, it doesn’t guarantee that another customer isn’t pointing to your endpoint.” Every Salesforce customer sends from the same ranges. So the listener also checks the org ID in the message, and treats anything it didn’t expect as suspect. Chapter 13 goes further with certificates.

Rarely. For a new design, publish a platform event instead (chapter 11): the other system subscribes, events can be replayed, and the message isn’t bound to a SOAP listener. If the other system only takes calls, call it directly or through the integration layer, with the tools from modules 2 to 5. But when you inherit an outbound message that works, you don’t have to rip it out. Know its limits, make sure its callback uses OAuth, and put it on the list for when that integration is next rebuilt.

  • For every outbound message you inherit: what happens if the listener is down for more than 24 hours, how it handles duplicates and order, and who notices.
  • The callback’s key: its own External Client App and integration user, with only the access it needs. No session IDs.
  • How the listener knows it’s really your org: the IP ranges, TLS, and the org ID.
  • For new work: a platform event or a direct callout, not a new outbound message.

Test knowledge. Know that outbound messages are SOAP, sent from a flow through Salesforce’s outbound message queue to a listener you build from Salesforce’s WSDL, which must acknowledge each message. Know they carry fields of the changed record only (not its children), up to 100 notifications per message, retried for 24 hours, possibly out of order or more than once. Know that the listener calls back with OAuth: session IDs are no longer sent.