Authentication

Create API keys, send them with each request, choose scopes and handle authentication errors.

Every request to the Signal REST API is authenticated with an API key. A key belongs to one workspace: every request made with it reads and writes that workspace's data, and nothing else. A key stands for the workspace, not for a person, so records it creates aren't credited to anyone unless you say who (see Assigned To and owner fields).

Create a key#

  1. In Signal, open Autopilot in the sidebar.
  2. Select the Webhooks & API tab, then API Keys.
  3. Select New Key.
  4. Enter a Key Name that says where the key is used (for example "Production integration").
  5. Tick the Scopes the key needs. Give each key only the scopes its integration uses.
  6. Select Create Key.
  7. Copy the key from the Save your API key dialog and store it somewhere safe, such as your secrets manager. This is the only time Signal shows the full key. Afterward the list shows only its first characters.

Who can create keys#

  • Anyone whose role can view integrations can see the workspace's keys (name, first characters, scopes, status, created and last-used dates).
  • Only people whose role can manage integrations can create or revoke keys. Workspace admins can by default.
  • A key can do no more than the person who creates it. For the scopes below that map to a role permission, the creator must hold that permission:
Scope Creator needs
accounts:read Read Accounts
accounts:write Create, update and delete Accounts
contacts:read Read Contacts
contacts:write Create, update and delete Contacts
deals:read Read Deals
deals:write Create, update and delete Deals
activities:read Read activities
activities:write Create, update and delete activities
users:read Read team members
marketing:read A plan that includes Marketing, Marketing switched on for their role, and read access to Marketing
marketing:write A plan that includes Marketing, Marketing switched on for their role, and update access to Marketing

The Marketing scopes are offered only when your plan includes Marketing.

Key format#

Keys look like sk_live_ followed by 64 hexadecimal characters:

sk_live_3b1f0c9e7a2d4e6f8a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f

Signal stores only a hash of the key, so a lost key can't be recovered. Create a new one instead.

Keys created in Signal don't expire. They work until you revoke them.

Send the key#

Send the key on every request in either of these headers. Both work the same way.

Authorization header (recommended):

cURL
curl https://crm.empiraa.app/api/v1/me \
  -H "Authorization: Bearer YOUR_API_KEY"

X-API-Key header:

curl https://crm.empiraa.app/api/v1/me \
  -H "X-API-Key: YOUR_API_KEY"

If you send both, the Authorization: Bearer value is used.

Keep keys on your server. Don't put them in browser code, mobile apps or public repositories, where anyone can read them.

Scopes#

Each key carries a list of scopes. An endpoint checks for its scope on every request and answers 403 if the key doesn't have it. GET /me needs no scope.

Scope Allows Endpoints
accounts:read Read Accounts and their spend records GET /accounts, GET /accounts/{id}, GET /accounts/{id}/spends, GET /accounts/{id}/spends/{spendId}
accounts:write Create, update and deactivate Accounts; add, change and delete spend records POST /accounts, PATCH /accounts/{id}, DELETE /accounts/{id}, POST /accounts/{id}/spends, PATCH /accounts/{id}/spends/{spendId}, DELETE /accounts/{id}/spends/{spendId}
contacts:read Read Contacts GET /contacts, GET /contacts/{id}
contacts:write Create, update and delete Contacts POST /contacts, PATCH /contacts/{id}, DELETE /contacts/{id}
deals:read Read Deals GET /deals, GET /deals/{id}
deals:write Create, update and delete Deals POST /deals, PATCH /deals/{id}, DELETE /deals/{id}
pipeline:read Read pipelines and their stages GET /pipelines, GET /pipelines/{id}
pipeline:write Create, rename and delete pipelines POST /pipelines, PATCH /pipelines/{id}, DELETE /pipelines/{id}
prospects:read Read prospect contacts, Prospect Companies and prospect sources GET /prospects, GET /prospects/{id}, GET /prospect-companies, GET /prospect-companies/{id}, GET /prospect-sources, GET /prospect-sources/{id}
prospects:write Create, update and delete prospect contacts and Prospect Companies POST /prospects, PATCH /prospects/{id}, DELETE /prospects/{id}, POST /prospect-companies, PATCH /prospect-companies/{id}, DELETE /prospect-companies/{id}
activities:read Read activities and calls GET /activities, GET /activities/{id}, GET /calls, GET /calls/{id}
activities:write Create, update and delete activities; log calls POST /activities, PATCH /activities/{id}, DELETE /activities/{id}, POST /calls, POST /call-logs
tasks:read Read the /tasks list GET /tasks, GET /tasks/{id}
tasks:write Create, update and delete items on the /tasks list POST /tasks, PATCH /tasks/{id}, DELETE /tasks/{id}
notes:read Read notes on prospect contacts GET /notes, GET /notes/{id}
notes:write Create, edit and delete notes POST /notes, PATCH /notes/{id}, DELETE /notes/{id}
bookings:read Read Scheduler bookings GET /bookings, GET /bookings/{id}
bookings:write Create, update and cancel bookings POST /bookings, PATCH /bookings/{id}, DELETE /bookings/{id}
decks:read Read Signal Decks GET /decks, GET /decks/{id}
decks:write Create, update and delete Decks POST /decks, PATCH /decks/{id}, DELETE /decks/{id}
users:read Read the workspace's users GET /users, GET /users/{id}
portfolio:read Read portfolio summary figures GET /portfolio
marketing:read Read Campaigns (needs a plan that includes Marketing) GET /marketing/campaigns
marketing:write Reserved. No endpoint uses it yet. None
property:read Read developments, plots, reservations and call-offs GET /property/developments, GET /property/developments/{id}, GET /property/plots, GET /property/plots/{id}, GET /property/reservations, GET /property/reservations/{id}, GET /property/call-offs, GET /property/call-offs/{id}
property:write Create, update and delete developments, plots and reservations; book call-offs and record their invoices and payments POST, PATCH and DELETE on /property/developments, /property/plots and /property/reservations; POST /property/call-offs, PATCH /property/call-offs/{id}

A key can't be given new scopes after it's created. To change what a key can do, create a new key with the scopes you want and revoke the old one.

Rotate a key#

Keys don't expire, so rotate them on your own schedule and whenever someone who had access leaves:

  1. Create a new key with the same scopes.
  2. Deploy the new key to your integration.
  3. Check the old key's last used time in the API Keys list stops changing.
  4. Revoke the old key.

Revoke a key#

In Autopilot → Webhooks & API → API Keys, select the revoke button on the key's row and confirm with Revoke. Revoking takes effect on the next request: the key is rejected with 401 INVALID_API_KEY. A revoked key stays in the list, marked Revoked, and can't be turned back on.

Authentication errors#

Errors use the problem details format.

401 Unauthorized#

No key was sent:

JSON
{
  "type": "https://signal.empiraa.com/errors/missing-api-key",
  "title": "Missing API key",
  "status": 401,
  "code": "MISSING_API_KEY",
  "detail": "Provide a key via Authorization: Bearer or X-API-Key"
}

The key doesn't exist, was revoked or has expired:

JSON
{
  "type": "https://signal.empiraa.com/errors/invalid-api-key",
  "title": "Invalid or revoked API key",
  "status": 401,
  "code": "INVALID_API_KEY"
}

403 Forbidden#

The key is valid but lacks the endpoint's scope. detail names the scope it needs:

JSON
{
  "type": "https://signal.empiraa.com/errors/insufficient-scope",
  "title": "Insufficient scope",
  "status": 403,
  "code": "INSUFFICIENT_SCOPE",
  "detail": "This key requires the 'deals:write' scope"
}

GET /marketing/campaigns also answers 403 with code PLAN_FEATURE_REQUIRED when the workspace's plan no longer includes Marketing. A key keeps working as a key after a downgrade, but the Marketing endpoint stops answering. See Marketing Campaigns.

402 Payment Required#

Write requests (POST, PATCH, DELETE) answer 402 WORKSPACE_PAYMENT_REQUIRED when the workspace's subscription doesn't currently allow changes, for example after a failed payment. Reads keep working. Update the billing details in Signal to restore writes.

JSON
{
  "type": "https://signal.empiraa.com/errors/workspace-payment-required",
  "title": "Workspace payment required. Please update your billing details.",
  "status": 402,
  "code": "WORKSPACE_PAYMENT_REQUIRED"
}