Skip to content

Quickstart

Five requests, shown in cURL, JavaScript and Python. At the end you have a key, a list of contacts and a webhook that tells you when a lead is qualified.

  • You are owner or admin of your WhatSetter workspace. The API page says it: Only owners and admins can manage API keys.
  • You have curl, Node.js 18 or newer, or Python 3 with requests.
  • The base URL is https://app.whatsetter.com/api/v1.
  1. Open Settings, then API & MCP.
  2. Click Create a key.
  3. Fill in Key name (for example CRM sync) and choose the Permissions: Full access, or Custom to pick scopes one by one.
  4. Click Create the key.
  5. Copy the key. The dialog reads Copy your key now. It will never be shown again. Then click I saved my key.

Store it in an environment variable, never in your code:

Terminal window
export WHATSETTER_API_KEY="ws_live_…"

GET /me needs no scope. It tells you which workspace and which permissions the key has.

Terminal window
curl https://app.whatsetter.com/api/v1/me \
-H "Authorization: Bearer $WHATSETTER_API_KEY"
{
"data": {
"team_id": "6b0c1d2e3f405162738a",
"key": {
"id": "66f1a2b3c4d5e6f7a8b9c0d1",
"name": "CRM sync",
"scopes": ["leads:read", "leads:write", "lists:write", "webhooks:manage"]
}
}
}

A 401 invalid_api_key means the key was mistyped or revoked. A 401 missing_api_key means the header did not reach us.

GET /leads needs leads:read. Filter by status, campaign_id, phone, source or since, and page with limit (1 to 100) and cursor.

Terminal window
curl "https://app.whatsetter.com/api/v1/leads?status=qualified&limit=10" \
-H "Authorization: Bearer $WHATSETTER_API_KEY"
{
"data": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0d1",
"phone": "33612345678",
"name": "Julien",
"status": "qualified",
"qualified": true,
"qualified_at": "2026-09-18T09:12:41.000Z",
"campaign_id": "acme-france-1-ab12",
"source": "list",
"tags": ["hot"],
"messages_count": 14,
"last_message_at": "2026-09-18T09:12:00.000Z",
"last_message_direction": "inbound"
}
],
"pagination": { "has_more": false, "next_cursor": null }
}

The lead object is shortened here. When has_more is true, send next_cursor back as cursor to get the next page.

Two calls: POST /lists creates an empty list, then POST /lists/{id}/leads pushes up to 500 contacts per call. Both need lists:write. Phones are deduplicated inside the batch and against the list.

Terminal window
# 1. Create the list
curl -X POST https://app.whatsetter.com/api/v1/lists \
-H "Authorization: Bearer $WHATSETTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"CRM sync, hot leads"}'
# 2. Push contacts into it (replace LIST_ID with the id from step 1)
curl -X POST https://app.whatsetter.com/api/v1/lists/LIST_ID/leads \
-H "Authorization: Bearer $WHATSETTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"leads":[{"phone":"+33612345678","name":"Julien","custom_variables":{"city":"Lyon"}}]}'
{
"data": {
"imported": 1,
"duplicates_in_list": 0,
"rejected": [],
"lead_ids": ["66f1a2b3c4d5e6f7a8b9c0d2"]
}
}

POST /webhooks needs webhooks:manage. The endpoint must be HTTPS.

Terminal window
curl -X POST https://app.whatsetter.com/api/v1/webhooks \
-H "Authorization: Bearer $WHATSETTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-server.com/webhooks/whatsetter","events":["contact.qualified","contact.not_qualified"]}'
{
"data": {
"id": "66f1c0ffee00000000000abc",
"url": "https://your-server.com/webhooks/whatsetter",
"events": ["contact.qualified", "contact.not_qualified"],
"active": true,
"description": null,
"secret_prefix": "whsec_3f9a",
"secret": "whsec_3f9a…",
"last_delivery_at": null,
"last_delivery_status": null,
"created_at": "2026-09-19T10:00:00.000Z"
}
}

The secret is shown only in this response. Store it: you need it to verify the signature of every delivery. Then send yourself a signed test event with POST /webhooks/{id}/test.