Example file

send-event.mjs

Node: signs and sends a sale from your CRM to an inbound custom integration's delivery URL.

send-event.mjs · JavaScript (Node) Download
// 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");

Stuck, or something missing?

Get in touch and a person on the team will help.

Contact us