Example file
send-event.mjs
Node: signs and sends a sale from your CRM to an inbound custom integration's delivery URL.
// Sends one signed event to a Subcontext inbound custom integration, in Node 18
// or later with no packages. Here it's a sale from your CRM, which Subcontext
// records as an outcome on the lead.
//
// Set these first (all three are on the integration's page in Subcontext):
// SUBCONTEXT_DELIVERY_URL the Delivery URL
// SUBCONTEXT_SIGNING_SECRET the value of the secret variable chosen as the Signing secret variable
// SUBCONTEXT_SIGNATURE_HEADER the Signature header (X-Integrations-Signature unless you changed it)
//
// Run it: node send-event.mjs
import { createHmac, randomUUID } from "node:crypto";
const DELIVERY_URL = process.env.SUBCONTEXT_DELIVERY_URL;
const SECRET = process.env.SUBCONTEXT_SIGNING_SECRET;
const HEADER = process.env.SUBCONTEXT_SIGNATURE_HEADER || "X-Integrations-Signature";
if (!DELIVERY_URL || !SECRET) throw new Error("Set SUBCONTEXT_DELIVERY_URL and SUBCONTEXT_SIGNING_SECRET.");
// The shape is up to you. The integration's settings say where to find each part.
// This one fits an Event type path of "type" and the outcome mapping on the
// Integrations guide.
const event = {
event_id: randomUUID(), // send the same id again if you retry, so it's recorded once
type: "deal.won",
deal: {
id: "DEAL-123",
contact_id: "YOUR_CRM_CONTACT_ID", // your id for the lead, or send ours (contact_id) instead
closed_at: new Date().toISOString(),
commission: "450.00",
},
};
export function signatureHeader(rawBody, secret, timestamp = Math.floor(Date.now() / 1000)) {
const digest = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
return `t=${timestamp},v1=${digest}`;
}
// Sign the exact string you send.
const body = JSON.stringify(event);
const response = await fetch(DELIVERY_URL, {
method: "POST",
headers: { "Content-Type": "application/json", [HEADER]: signatureHeader(body, SECRET) },
body,
});
// 200: accepted. 400: not JSON. 403: signature, secret or clock problem. 404: wrong URL.
console.log(response.status, response.status === 200 ? "accepted" : "not accepted");