Learn how to send Salesforce record changes to Nango from an Apex trigger and verify them
Salesforce has no outbound webhooks that Nango can subscribe to. Instead, you install an Apex trigger in each connected org that calls your Nango webhook URL when records change. Because the call comes from code you install, not from Salesforce, there is no provider signature. Nango verifies each call with a secret that belongs to the connection instead.
An Apex trigger in the connected org sends a POST request to your Nango webhook URL when a record changes.
Nango reads the connection id from nango.connectionId in the body and checks the X-Nango-Webhook-Secret header against that connection’s webhookSecret metadata.
Nango routes the event to the connection, using nango.eventType as the webhook type.
A request without the header, with a wrong secret, or for a connection that has no webhookSecret is rejected. Every connection needs its own secret. There is no integration level secret, because it would sit in every org’s trigger and any org admin could use it to send events for other connections.
Generate a random secret of at least 16 characters when the connection is created and store it as webhookSecret in the connection metadata. A post-connection-creation function does this for every new connection:
import { randomBytes } from 'crypto';import { createOnEvent } from 'nango';export default createOnEvent({ event: 'post-connection-creation', description: 'Generate the webhook secret the Apex trigger sends to Nango', exec: async (nango) => { await nango.updateMetadata({ webhookSecret: randomBytes(32).toString('hex') }); }});
Read it back with Get connection when you install the trigger. To set or rotate it yourself instead:
Copy the Nango webhook URL from your Salesforce integration’s Webhooks section in the Nango dashboard, and add its host as a Remote Site Setting in the org so Apex can call it.Store the webhook URL, the connection id and the secret where the trigger can read them but regular users cannot, for example a protected custom setting in a managed package. The example below uses a Nango_Settings__c hierarchy custom setting:
public class NangoWebhook { @future(callout=true) public static void send(String eventType, List<Id> recordIds) { Nango_Settings__c settings = Nango_Settings__c.getOrgDefaults(); HttpRequest req = new HttpRequest(); req.setEndpoint(settings.Webhook_Url__c); req.setMethod('POST'); req.setHeader('Content-Type', 'application/json'); req.setHeader('X-Nango-Webhook-Secret', settings.Webhook_Secret__c); req.setBody(JSON.serialize(new Map<String, Object>{ 'nango' => new Map<String, Object>{ 'connectionId' => settings.Connection_Id__c, 'eventType' => eventType }, 'recordIds' => recordIds })); new Http().send(req); }}
trigger AccountChanged on Account (after insert, after update) { NangoWebhook.send(Trigger.isInsert ? 'account.created' : 'account.updated', new List<Id>(Trigger.newMap.keySet()));}
Admins of the org can read the secret wherever the trigger can. That is why each connection has its own: a leaked secret only lets someone send events for that one connection.