> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.sailorhq.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sailorhq.io/_mcp/server.

# Quickstart

> Create an outcome, receive a webhook, and verify the phone-system loop.

This quickstart builds the smallest complete phone-system loop: create an action called `Interested`, receive the webhook when an agent applies it, then add a post-call disposition so the workflow has a required final classification.

You will finish with:

* One action outcome agents can use during a call.
* One signature-verifying webhook receiver that returns `2xx`.
* One disposition outcome that turns on the post-call disposition step.

## Prerequisites

* A Sailor API key created by an owner or admin in **Settings → Integrations → API access**. Production keys begin with `sl_live_` and are revealed once.
* A Smart List ID from the workspace you want to update.
* An HTTPS endpoint that can receive `POST` requests.
* A webhook signing secret created in **Settings → Integrations → Webhook signing**.

## 1. List Existing Outcomes

Start by reading the current configuration.

```bash
curl https://api.sailorhq.io/phone-system/outcomes \
  -H "Authorization: Bearer $SAILOR_API_TOKEN"
```

The response includes `disposition_popup_enabled`. It becomes `true` when at least one post-call disposition outcome is configured.

## 2. Create An Action Outcome

Action outcomes are available during a live call. They require a `webhook_url` because Sailor posts an event when the action runs.

```bash
curl https://api.sailorhq.io/phone-system/outcomes \
  -X POST \
  -H "Authorization: Bearer $SAILOR_API_TOKEN" \
  -H "Idempotency-Key: $SAILOR_REQUEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "outcome_name": "Interested",
    "outcome_type": "action",
    "order": 10,
    "destination_type": "smart_list",
    "destination_id": "00000000-0000-4000-8000-000000000000",
    "webhook_url": "https://example.com/sailor/outcomes"
  }'
```

Sailor returns `201 Created` with the created `outcome` and the current `disposition_popup_enabled` value.

```json
{
  "ok": true,
  "outcome": {
    "outcome_id": "00000000-0000-4000-8000-000000000001",
    "outcome_name": "Interested",
    "outcome_type": "action",
    "order": 10,
    "destination_type": "smart_list",
    "destination": {
      "destination_type": "smart_list",
      "destination_id": "00000000-0000-4000-8000-000000000000",
      "destination_name": "Follow up"
    },
    "webhook_url": "https://example.com/sailor/outcomes",
    "created_at": "2026-06-25T00:00:00.000Z",
    "updated_at": "2026-06-25T00:00:00.000Z"
  },
  "disposition_popup_enabled": false
}
```

## 3. Receive The Webhook

Your endpoint should accept a JSON body and return any `2xx` status.

**`express-example.js`**

```javascript title="express-example.js"
import express from "express";

const app = express();

app.post("/sailor/outcomes", express.raw({ type: "application/json" }), async (req, res) => {
  // Verify X-Sailor-Signature against req.body's exact bytes before parsing.
  // See the complete constant-time example in the Webhooks guide.
  const payload = JSON.parse(req.body.toString("utf8"));

  // Store or enqueue the event before returning 2xx.
  console.log("Sailor outcome received", {
    outcome: payload.outcome?.outcome_name,
    call: payload.call?.call_id,
    destination: payload.destination?.destination_type,
  });

  res.sendStatus(204);
});

app.listen(3000);
```

> **Warning**
>
> Do not process an unverified body. After verification, persist the unique `X-Sailor-Delivery-Id`, return `2xx`, then do slow downstream work asynchronously.

## 4. Add A Post-Call Disposition

Disposition outcomes are shown after a call. Use them for required post-call classification.

```bash
curl https://api.sailorhq.io/phone-system/outcomes \
  -X POST \
  -H "Authorization: Bearer $SAILOR_API_TOKEN" \
  -H "Idempotency-Key: $SAILOR_REQUEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "outcome_name": "Not Qualified",
    "outcome_type": "disposition",
    "order": 20,
    "destination_type": "none"
  }'
```

After this, `GET /phone-system/outcomes` should return `disposition_popup_enabled: true`.

## 5. Verify The Loop

#### Confirm the outcome exists

Call `GET /phone-system/outcomes` and verify the new outcome is present.

#### Apply the outcome from Sailor

Use the phone-system workflow to apply the outcome to a call.

#### Check your receiver logs

Confirm your webhook endpoint received the outcome payload and returned a `2xx` status.

#### Check destination behavior

If the outcome targets a Smart List, confirm the contacted person moved as expected.

## Done When

| Check                        | Expected result                                        |
| ---------------------------- | ------------------------------------------------------ |
| `GET /phone-system/outcomes` | Returns the new action and disposition.                |
| `disposition_popup_enabled`  | `true` after the disposition exists.                   |
| Webhook receiver             | Logs the Sailor payload and returns `2xx`.             |
| Smart List                   | The contacted person moves when the action is applied. |

## Next Steps

* Read [Core concepts](/phone-system/concepts) for outcome types and destinations.
* Use [Outcomes](/phone-system/outcomes) for update and delete examples.
* Use [Webhooks](/phone-system/webhooks) for payload details.
* Use [Rate limits](/rate-limits) to implement bounded retries.