Data 360
Data 360 webhook data actions: 'Signing Key Not Found' and validating the signature
Your Data 360 data action is configured, the streaming insight fires, and your endpoint receives nothing. The logs say 'Signing Key Not Found'. Here is why a webhook target stays silent until you generate its secret key, how the HMAC-SHA256 signature works, and the raw-body mistake that makes a correct validation check fail.
You built a data action to push an event out of Data 360 the moment a streaming insight trips. You pointed it at a webhook target, saved it, triggered the condition, and your external endpoint received nothing. No request, no error at your end, just silence. Then you open the data action’s run history and find the calls marked Signing Key Not Found.
Nothing is broken in your code. The target is doing exactly what it is designed to do when a step is missing: refusing to send. A webhook data action will not deliver a single payload until you generate the secret key it signs with, and skipping that step is the most common reason a freshly built target sits dead.
So the whiteboard version, before the detail. Generate the secret key on the webhook target, copy it to your endpoint, and validate each delivery by recomputing an HMAC-SHA256 over the exact bytes you received and comparing it to the base64 x-signature header.
The second most common failure is doing that validation against a re-serialised body instead of the raw one, which produces a signature that never matches. Both are avoidable once you know how the signing works.
What a webhook data action is, in one paragraph
A data action in Data 360 publishes an event to an external destination when something inside the platform fires, most often a calculated or streaming insight crossing a threshold or a monitored change. It is the platform’s outbound-event mechanism, the counterpart to the inbound streams that feed the lake.
A data action sends to a data action target, and there are three kinds: a Salesforce Platform Event, a Webhook, and Marketing Cloud. The Platform Event target keeps everything inside the Salesforce event bus, which pairs naturally with the Pub/Sub API. The webhook target is the one you reach for when the consumer is an external service that speaks plain HTTPS, and it is the one with the signing step.
Why “Signing Key Not Found” happens
The webhook target signs every payload so your endpoint can prove the request came from Salesforce and not from anyone who guessed your URL. That signature is computed with a secret key, and the key does not exist until you create it.
If no secret key has been generated on the target, the platform has nothing to sign with, so it declines to send and records Signing Key Not Found against the call. The error is not about your endpoint being unreachable or returning an error. The request never left Salesforce.
The fix is a single step in Setup. Open the Data Action Targets tab, open your webhook-based target, and generate the secret key. Copy the generated key somewhere your webhook system can read it, because your endpoint needs the same key to validate what arrives. After you generate or regenerate a key, give it up to 15 minutes to take effect before you expect deliveries to flow.
That 15-minute window catches people twice: once here, and again at rotation. If you regenerate the key and immediately fire a test, the old signing context may still be active, so a mismatch right after a rotation is usually impatience, not a bug.
How the signature works
Once a secret key exists, every payload the target sends carries a signature in an HTTP header named x-signature. The value is the HMAC-SHA256 of the payload, computed with your secret key and encoded as base64. A real one looks like this:
x-signature: uruO+yjX8KKjl72GkQbLXuoMyAilC5losEnY1Liiu30=
Your endpoint validates a delivery by doing the same computation and checking that the result matches. Recompute the HMAC-SHA256 of the received body using the secret key, base64-encode it, and compare it to the x-signature value. If they match, the request is authentic. If they don’t, reject it before you act on anything inside it.
Compare the two strings in constant time. A naive == on the signature leaks timing information that a determined attacker can use to forge a valid value byte by byte, so use a constant-time comparison built for the purpose.
Here is a receiver in Node that does it correctly:
import express from 'express';
import crypto from 'node:crypto';
const app = express();
// Capture the RAW body. Don't let a JSON parser touch it before you verify:
// re-serialising the parsed object changes the bytes and breaks the signature.
app.use(express.raw({ type: '*/*' }));
const SECRET = process.env.DATA_ACTION_SECRET; // the key you generated on the target
app.post('/data360/webhook', (req, res) => {
const sent = req.get('x-signature'); // base64 HMAC-SHA256 from Data 360
if (!sent) return res.status(401).end();
const expected = crypto
.createHmac('sha256', SECRET)
.update(req.body) // req.body is a Buffer of the raw bytes
.digest('base64');
const a = Buffer.from(sent);
const b = Buffer.from(expected);
const ok = a.length === b.length && crypto.timingSafeEqual(a, b);
if (!ok) return res.status(401).end();
const event = JSON.parse(req.body.toString('utf8')); // parse only after verifying
// ... handle the event, then acknowledge quickly
res.status(200).end();
});
The raw-body mistake that fails a correct check
Read the comment in that handler again, because it is the part that costs people an afternoon. The signature is computed over a specific sequence of bytes. Your validation has to hash the same bytes, and the trap is that most web frameworks parse the JSON body for you before your code runs.
Once a framework has parsed the payload into an object and you re-serialise it to a string to hash, you have almost certainly changed the bytes. Key order can shift, whitespace disappears, and unicode or slash escaping differs from what Salesforce produced.
Salesforce’s own signature depends on the exact JSON escaping of the payload it sent. A body that is semantically identical but byte-different produces a different HMAC and fails validation every time, even though your key and algorithm are right.
The rule that avoids it: capture and hash the raw request body exactly as received, and parse it into an object only after the signature checks out. In Express that means express.raw() rather than express.json() on the webhook route.
In other stacks it means reaching for the raw body buffer the framework exposes, or disabling body parsing on that one path. When a validation fails and you are certain the key is right, the re-serialised body is the first thing to check.
Rotation and the operational edges
Treat the secret key like any other credential. Salesforce recommends regenerating it at least every 12 months, and the same 15-minute propagation applies to a rotation, so plan for a short overlap rather than a hard cutover. If your endpoint can hold two valid keys briefly during a rotation, you avoid dropping deliveries in that window.
A few more edges worth knowing before you go live:
- Return a fast 2xx. Do the real work asynchronously after acknowledging, so a slow downstream call doesn’t hold the connection open and invite retries.
- Data actions and their targets sit under documented per-tenant limits and behaviours; check the current “Streaming Insights and Data Actions Limits and Behaviors” page in Help before you fan out dozens of targets, since the ceilings shape how you consolidate them.
- A newly created action can take up to 15 minutes before its target starts receiving events, which is the same propagation delay as the key, so a quiet target in the first few minutes after setup is expected, not a failure.
When a webhook is the wrong target
The webhook target earns its place when the consumer lives outside Salesforce and you want a signed, real-time push. When the consumer is inside the platform, the other two targets are usually cleaner.
A Platform Event target keeps the event on the bus for Apex or Flow subscribers with no signature handling at all. For automation that should run inside Salesforce, a Data 360-triggered Flow skips the outbound hop entirely.
Reach for the webhook when the destination is genuinely external and you are prepared to own the endpoint: its availability, its signature check, and its retry behaviour. That ownership is the price of leaving the platform, and it is the same discipline that separates a webhook you can operate from one that silently drops events. If you are wiring several of these, treat them as first-class integrations with monitoring, not as fire-and-forget callouts.
Where to start when a target is silent
Two checks resolve most of these. If the run history says Signing Key Not Found, you never generated the secret key, so generate it and wait fifteen minutes. If deliveries arrive but your endpoint rejects them, you are almost certainly hashing a re-serialised body instead of the raw one, so switch to the raw bytes and validate again. Get past those two and a Data 360 webhook target is a boring, dependable way to push a signed event to anything that can receive one.
Understanding the basics
Why does my Data 360 data action say “Signing Key Not Found”?
Because the webhook target has no secret key generated, and the platform will not send an unsigned payload. Open the Data Action Targets tab, open the webhook target, and generate the secret key, then copy it to your endpoint for validation. Allow up to 15 minutes for the key to take effect before deliveries flow.
How does Data 360 sign webhook payloads?
Each payload carries an x-signature header whose value is the HMAC-SHA256 of the payload, computed with your target’s secret key and base64-encoded. Your endpoint recomputes the same HMAC over the received body and compares it to the header. A constant-time comparison is the correct way to check equality.
My key and algorithm are right but validation still fails. Why?
You are most likely hashing a re-serialised copy of the body instead of the exact bytes that arrived. Frameworks that parse JSON automatically change the byte sequence when you re-serialise, and the signature depends on the exact escaping Salesforce sent. Capture the raw request body, validate against it, and parse to an object only after the signature matches.
What are the three Data 360 data action targets?
Platform Event, Webhook, and Marketing Cloud. Platform Event keeps the event inside the Salesforce event bus, Webhook pushes a signed HTTPS request to an external system, and Marketing Cloud routes into that platform. Choose the webhook target when the consumer is external and needs a real-time, signed push.
How often should I rotate the secret key?
Salesforce recommends regenerating it at least every 12 months. A regeneration takes up to 15 minutes to take effect, so plan a short overlap and, if you can, let your endpoint accept the old and new key during that window to avoid dropped deliveries.
Standing up outbound events from Data 360 and want them signed, monitored, and safe to depend on? Talk to us. Getting the platform’s real-time events to land reliably in the systems around it is the kind of integration work we do every week.