Authentication

Sailor public API requests use a one-workspace API key. An owner or admin creates the key in Settings → Integrations → API access. Sailor shows the secret once; store it before closing the dialog.

Production keys begin with sl_live_. Send the key only from a trusted backend. Do not put it in browser code, mobile clients, extensions, public repositories, analytics, or support screenshots.

Every request should include Authorization: Bearer $SAILOR_API_TOKEN.

Request Shape

$curl https://api.sailorhq.io/phone-system/outcomes \
> -H "Authorization: Bearer $SAILOR_API_TOKEN" \
> -H "Content-Type: application/json"

Workspace And Permission Scope

Every key is permanently attached to the workspace where it was created. A request cannot select or override another organization or workspace. Choose the minimum required scopes when creating the key:

ScopeAllows
phone_system.outcomes:readList phone-system outcomes.
phone_system.outcomes:writeCreate, update, and delete phone-system outcomes.
staff:writeCreate Sailor staff invitations in this exact workspace.
smart_lists:readList Smart Lists and inspect member state in this exact workspace.
smart_lists:writeCreate, update, clone, delete, and populate Smart Lists in this exact workspace.

Sailor returns RFC 9457 problem details as application/problem+json when authentication or authorization fails.

Treat the API key as a workspace-scoped secret. A public key leak is a workspace incident: revoke or rotate it immediately.

Write Requests

Create and update requests require an Idempotency-Key header. Use an opaque 8–255 character value that is stable for one logical operation. Retrying the same operation with the same key returns the stored response; using that key with different request data returns 422 idempotency_key_reused.

$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",
> "destination_type": "smart_list",
> "destination_id": "00000000-0000-4000-8000-000000000000",
> "webhook_url": "https://example.com/sailor/outcomes"
> }'

Header Checklist

RequestRequired headers
GET /phone-system/outcomesAuthorization
POST /phone-system/outcomesAuthorization, Idempotency-Key, Content-Type
PATCH /phone-system/outcomes/{outcomeId}Authorization, Idempotency-Key, Content-Type
DELETE /phone-system/outcomes/{outcomeId}Authorization; Idempotency-Key is optional but recommended for retries.
POST /staff/invitesAuthorization, Idempotency-Key, Content-Type
GET /smart-listsAuthorization
GET /smart-lists/{smartListId} and member readsAuthorization
POST /smart-listsAuthorization, Idempotency-Key, Content-Type
PATCH /smart-lists/{smartListId}Authorization, Idempotency-Key, Content-Type
Smart List clone and member mutationsAuthorization, Idempotency-Key, Content-Type
DELETE /smart-lists/{smartListId}Authorization, Idempotency-Key

Idempotency Keys

SituationUse this key
Retrying the same logical operationReuse the same Idempotency-Key.
Starting a new intended operationGenerate a new Idempotency-Key.

If Sailor returns 409 request_in_progress, wait for the Retry-After delay, then retry the same logical operation with the same key. A replayed response includes Idempotent-Replayed: true.

Common Auth Errors

ErrorMeaningWhat to check
unauthorizedSailor could not authenticate the request.Confirm the bearer key is complete, live, unexpired, and not revoked.
insufficient_scopeThe key is valid but lacks the operation’s scope.Create or rotate a key with the documented scope.
feature_not_availablePhone-system outcomes are not enabled for the key’s workspace.Enable the phone-system feature in that same workspace.
rate_limit_exceededA credential or workspace quota is exhausted.Wait for Retry-After; inspect RateLimit and RateLimit-Policy.
temporarily_unavailableA deadline, concurrency lease, or durable control could not safely complete.Wait for Retry-After and retry idempotently.

Problem responses have this stable shape:

1{
2 "type": "https://docs.sailorhq.io/problems/insufficient-scope",
3 "title": "Insufficient scope.",
4 "status": 403,
5 "code": "insufficient_scope",
6 "request_id": "2eab7d20-219b-4d17-9f4f-2c26c54d91c4",
7 "detail": "This operation requires phone_system.outcomes:write."
8}

Security Checklist

  • Store keys in server-side secret storage.
  • Rotate keys if they appear in logs, screenshots, or client bundles.
  • Use an IP allowlist when your outbound addresses are stable.
  • Set an expiration for temporary integrations.
  • Use HTTPS for webhook URLs and API calls.
  • Treat webhook payloads as operational events, not as an authentication mechanism.