Salesly API

Webhooks

Receive real-time event notifications from Salesly via webhooks.

Salesly can send webhook events to your server when certain actions occur in your account.

Supported Events

EventTrigger
order.finishedAn order reaches the "finished" state (state 6)
opportunity.finishedAn opportunity reaches a final state (state = 4 won or state = 5 lost)
opportunity.wonAn opportunity is marked as won (state = 4)
opportunity.lostAn opportunity is marked as lost (state = 5)

When an opportunity is closed, Salesly currently sends the generic opportunity.finished event and also the specific outcome event (opportunity.won or opportunity.lost).

Configuration

Configure your webhook endpoint in Settings → Integrations:

  1. Webhook URL — the HTTPS endpoint that will receive events
  2. Webhook Secret — used to sign payloads for verification

Payload Format

Webhook payload
{
  "event": "opportunity.won",
  "data": {
    "opportunity_id": 123,
    "name": "Website redesign",
    "enterprise_id": 456,
    "state": 4
  },
  "timestamp": "2026-03-05T14:30:00+00:00"
}

opportunity.finished is still emitted for backward compatibility when an opportunity closes. Salesly now also emits opportunity.won for state = 4 and opportunity.lost for state = 5.

order.finished uses the same envelope, but its data object looks like:

{
  "order_id": 987,
  "reference": "ORD-00042",
  "enterprise_id": 456,
  "state": 6
}

Signature Verification

Every webhook includes an X-Salesly-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook secret.

Always verify signatures before processing webhook payloads.

const crypto = require("crypto");

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express.js example
app.post("/webhooks/salesly", (req, res) => {
  const signature = req.headers["x-salesly-signature"];
  const body = JSON.stringify(req.body);

  if (!verifyWebhook(body, signature, process.env.SALESLY_WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }

  const { event, data } = req.body;
  console.log(`Received ${event}:`, data);

  res.status(200).send("OK");
});

Headers

HeaderDescription
X-Salesly-SignatureHMAC-SHA256 signature of the request body
X-Salesly-EventThe event type (for example order.finished or opportunity.won)
Content-Typeapplication/json

Best Practices

  • Always verify signatures — never process unverified payloads
  • Respond quickly — return 200 OK immediately, then process asynchronously
  • Handle duplicates — deduplicate using event plus the resource identifier inside data
  • Use HTTPS — webhook URLs must use HTTPS in production