Webhooks

Sailor sends an outcome webhook to the URL configured on an action outcome. Use the payload to update your CRM, trigger follow-up, audit list movement, or start downstream automation.

When Webhooks Are Sent

1

You create an action outcome

The outcome has outcome_type: "action" and a webhook_url.

2

An agent applies the outcome

The action is selected during a live call.

3

Sailor runs the destination action

Sailor records the outcome and attempts any configured Smart List movement.

4

Sailor posts the webhook

Your endpoint receives the outcome, call, scope, and destination details.

Return any 2xx status to acknowledge receipt. Do slow or unreliable downstream work after you have accepted the webhook.

Example Payload

1{
2 "error_message": "",
3 "timestamp": 1781935984000,
4 "scope": {
5 "subaccount_id": "00000000-0000-4000-8000-000000000001",
6 "organization_id": "00000000-0000-4000-8000-000000000002"
7 },
8 "outcome": {
9 "outcome_id": "00000000-0000-4000-8000-000000000003",
10 "outcome_name": "Interested",
11 "outcome_type": "action"
12 },
13 "call": {
14 "call_id": "00000000-0000-4000-8000-000000000004",
15 "phone_number": "+15555550100",
16 "contact_id": "00000000-0000-4000-8000-000000000005"
17 },
18 "destination": {
19 "destination_type": "smart_list",
20 "destination_id": "00000000-0000-4000-8000-000000000006"
21 }
22}

Payload Fields

FieldMeaning
timestampUnix epoch timestamp in milliseconds for the webhook event.
scope.subaccount_idWorkspace scope that owns the call and outcome.
scope.organization_idOrganization that owns the workspace.
outcomeOutcome ID, name, and type applied by the agent.
callCall ID, contacted phone number, and contact ID.
destinationSmart List destination action Sailor attempted, when configured.
error_messageError text if Sailor attempted the destination action and encountered a failure.

Destination Shapes

For an add-to-list outcome:

1{
2 "destination_type": "smart_list",
3 "destination_id": "00000000-0000-4000-8000-000000000006"
4}

For a remove-from-lists outcome:

1{
2 "destination_type": "remove_from_smart_list",
3 "selection": "selected",
4 "destination_ids": [
5 "00000000-0000-4000-8000-000000000010"
6 ]
7}

Receiver Checklist

  • Accept POST requests with Content-Type: application/json.
  • Return a 2xx status after you store or enqueue the event.
  • Make your receiver idempotent by keying on call.call_id and outcome.outcome_id.
  • Keep endpoint credentials and forwarding secrets out of browser code.
  • Log the raw payload while you are testing so operators can compare Sailor behavior with downstream state.

Minimal Receiver

server.js
1import express from "express";
2
3const app = express();
4app.use(express.json());
5
6async function saveOutcomeEvent(event) {
7 console.log("Accepted Sailor outcome event", event);
8}
9
10app.post("/sailor/outcomes", async (req, res) => {
11 const { outcome, call, destination } = req.body;
12
13 await saveOutcomeEvent({
14 callId: call.call_id,
15 outcomeId: outcome.outcome_id,
16 outcomeName: outcome.outcome_name,
17 destinationType: destination?.destination_type ?? "none",
18 });
19
20 res.sendStatus(204);
21});

Testing Locally

Use a tunnel during development so Sailor can reach your local server.

$ngrok http 3000

Set the outcome webhook_url to the public tunnel URL while testing, then switch it to your production HTTPS URL before using the outcome with live agents.

See the Webhooks section in the API Reference for the complete schema.