> ## Documentation Index
> Fetch the complete documentation index at: /help/llms.txt
> Use this file to discover all available pages before exploring further.

# API

> Everything you do in Kerux, from your own code.

> [!NOTE]
>
> **Pro plan.** API keys are part of Pro and Agency (and the free trial). On Starter, **Create key** shows **Part of Pro** with an **Upgrade to Pro** button, and the API answers `403 PRO_FEATURE` when a key creates something that is Pro. Anything you already have keeps working on every plan. See [Plans and billing](/help/account/billing.md).

The Kerux API uses the same rules as the app: contacts, lists, campaigns (including a test on one contact), enrollments, inbox (including LinkedIn invitations you received, and sending a waiting automatic answer now or canceling it), the pipeline, LinkedIn accounts, message templates (with their subject), email templates (the three builders, their versions and uploaded pictures), rules, webhooks and analytics (including InMails sent, their replies and how many went free). The full reference is at [usekerux.com/docs/api](https://www.usekerux.com/docs/api).

## Create an API key

**1. Open Developers**

Click **Integrations > Developers** (owners and admins only).

**2. Create a key**

Give it a name and pick its **Scopes**. A new key starts with only the read scopes ticked: tick the write scopes or **admin** yourself, only if it needs them. Click **Create key** and copy the key: it's shown once.

**3. Call the API**

```bash theme={null}
curl https://www.usekerux.com/api/v1/me \
  -H "Authorization: Bearer YOUR_KEY"
```

## Good to know

* A key acts as the person who created it, never beyond their role, and only within its permissions. It stops working if that person leaves the workspace.
* **Rate limit**: 600 requests per minute per key (see the `RateLimit-*` headers).
* **Safe retries**: send an `Idempotency-Key` header on writes; the same key within 24 hours returns the first answer instead of doing it twice.
* **Errors** look like `{ "error": { "code": "...", "message": "..." } }`.
* Lists use cursor pagination.
* LinkedIn limits apply to everything the API does, exactly as in the app.

## Some endpoints

* **Pipeline**: `GET /v1/pipeline` returns the board (every stage with its contacts, count and value), `PUT /v1/leads/{id}/stage` moves a contact to a stage or off the board (`null`) and sets `deal_value_cents`, and `PUT /v1/pipeline/stages` renames, adds, removes or reorders the stages (2 to 20, **admin** scope). Contacts have `stage` and `deal_value_cents`. See [Pipeline](/help/leads/pipeline.md).
* **Smart segments**: `GET` and `POST /v1/segments`, `GET`, `PATCH` and `DELETE /v1/segments/{id}`, `GET /v1/segments/{id}/leads` (the contacts in it now) and `POST /v1/segments/count` (how many contacts some filters match) (**leads:read** / **leads:write**). See [Smart segments](/help/leads/segments.md).
* **Broadcasts**: `GET` and `POST /v1/broadcasts`, `GET` and `PUT /v1/broadcasts/{id}` (a draft; once sent, `422` `BROADCAST_ALREADY_SENT`), `GET /v1/broadcasts/{id}/check` (the preflight and the forecast) and `POST /v1/broadcasts/{id}/send` (once) (**workflows:read** / **workflows:write**; sending starts the audience, so it needs **enrollments:write** too). See [Broadcasts](/help/campaigns/broadcasts.md).
* **Lead routing and engagement**: `GET` and `PUT /v1/workspace/lead-assignment` (`mode` `off`, `round_robin` or `least_open`, and `user_ids`) and `GET` and `PUT /v1/workspace/engagement` (points, `hot_threshold`, `decay_days`, `hot_alert`, `enabled`) (reads need **leads:read**, changes **admin**). `GET /v1/leads/{id}/engagement` returns a contact's score, `hot_at` and latest signals; `GET /v1/leads` takes `sort=engagement`, `hot` and `min_engagement`, and every contact has `engagement_score` and `engagement_hot_at`. See [Who gets new contacts, and hot contacts](/help/leads/routing-and-engagement.md).
* **Booking pages**: `GET` and `POST /v1/booking-links`, `GET`, `PATCH` and `DELETE /v1/booking-links/{id}` (title, `duration_minutes`, `assignment` `single` or `round_robin`, `hosts` with `user_id` and `mailbox_id`, `timezone`, `hours`, buffers, `min_notice_minutes`, `max_days_ahead`, `daily_cap`, `questions`, `conference`, `is_default`), `GET /v1/booking-links/{id}/availability?from=&to=` (free times, at most 31 days), `GET /v1/bookings` (`from`, `to`, `status`, `link_id`, `lead_id`), `POST /v1/bookings/{id}/cancel` and `POST /v1/bookings/{id}/review` (`check` or `release` a booking that needs a check). Reads need **leads:read**, changes **leads:write**. See [Your booking page](/help/campaigns/booking-page.md).
* **Hosted forms, surveys and quizzes**: `GET`, `PUT` and `DELETE /v1/triggers/{id}/form` read, change or remove the form on a trigger URL (**workflows:write** scope). Fields can have a `type` (`single_choice`, `multiple_choice`, `dropdown`, `rating`, `yes_no`; absent means free text) with `options` (each a `label` and optional quiz `points`) or a rating `scale` (5 or 10), `layout` `one_per_page` shows one question per page, and `quiz` (`field`, e.g. `custom.quiz_score`, and `show`) saves the total score. The answer gives the form and its public `form_url`. See [Surveys and quizzes](/help/campaigns/surveys-and-quizzes.md).
* **Business cards**: `POST /v1/business-cards/scan` reads a photo of one or more business cards (`image.media_type` and `image.data` in base64, JPG, PNG, WebP or GIF, up to 2.5 MB) into `drafts` and saves nothing. Each draft has its `fields`, the key fields the card didn't show (`missing`) and any `duplicate` already in your contacts (`by` `linkedin` or `email` for sure, `phone` or `name` probably). `status` is `read` or `partial`, or says why the drafts are empty (`unreadable`, `ai_off`, `ai_failed`, `ai_declined`, `quota`, `rate_limited`). `POST /v1/business-cards` saves one reviewed card (`fields`, optional `merge_into`, `list_id` or `new_list_name`, `campaign_id`, `tags`) and returns the `lead_id` and whether it was `created` or `updated`. Both need **leads:write**; a scan uses AI credits. See [Scan business cards](/help/leads/business-cards.md).
* **Business profile**: `GET /v1/workspace/business-profile` reads the text, the selling style and what your website says (**workflows:read** scope), and `PUT /v1/workspace/business-profile` changes the `text` (3,000 characters at most) and / or the `style` (`gentle`, `balanced` or `direct`), or forgets the website with `clear_website` (**admin** scope, owners and admins). See [Business profile](/help/account/business-profile.md).
* **Campaign review**: `GET /v1/workflows/{id}/review` returns what the builder's **Review** shows (**workflows:read** scope): the checks (`rules`, each finding with its `code`, its `level` `problem` or `improve` and the step's `node_id`, plus the `problems` and `improvements` counts, and `empty`: `true` while the campaign has no steps yet, so nothing is judged) and the last AI opinion (`ai`: `goal`, `rating` `strong`, `needs_work` or `weak`, `verdict` and its findings with any rewritten text, or `null`). `stale` is `true` when the campaign changed after that opinion. Asking the AI for a new opinion is in the app only. See [Review your campaign](/help/campaigns/review.md).
* **Campaign step changes**: `POST /v1/workflows/{id}/draft/changes` applies the same changes Ask AI proposes to the campaign's draft (**workflows:write** scope): `changes` is a list of up to 6, each with `op` (`add`, `edit` or `remove`), `where` (`after`, `before` or `end`) and `step` (the step's key or number), and for `add` the step's `type` (`message`, `email`, `inmail`, `wait` with `days`, `connect`, `tag`, `note`, `task`, `condition` with `check`, `integration` with `preset` `icypeas.find_email_and_wait`...) with its `text` and `subject`. It returns the `added`, `changed` and `removed` step ids, what was `refused` and why (`http`, `link`, `secret`, `channel`, `step`...) and whether the campaign is `live`. It never publishes: live contacts keep the published version until you publish. Webhook and HTTP steps, links, `secrets.*`, WhatsApp and SMS are refused here; use `PUT /v1/workflows/{id}/draft` for the full definition. See [Ask AI](/help/getting-started/ask-ai.md).
* **Why it's stuck, in plain words**: `GET /v1/leads/{id}/explain` says why a contact waits or is stuck in each campaign: `reason_code` and `reason` in plain words, since when, the `step` (its number on the canvas), what is `missing`, and the `fixes`, each with the API call that does it. `GET /v1/workflows/{id}/explain` says why a campaign is stuck or slow: `counts`, where contacts wait by step and reason, each LinkedIn account's usage against today's limits, and `next_steps` (**workflows:read** scope). `GET /v1/workflows/{id}` also returns `steps` (and `live_steps`) in plain words. Without **leads:read** the contact's own fields come back `null`; without **senders:read** the accounts' usage is left out.
* **Build and launch a campaign**: `POST /v1/workflows/from-description` builds a campaign draft from a `description` like **Create with AI** (**workflows:write** scope; one AI use; needs the business profile, `422 BUSINESS_PROFILE_NEEDED`); it returns the `steps` and the `notes` (what is left to do) and never launches. `POST /v1/workflows/{id}/draft/changes/preview` shows what `POST /v1/workflows/{id}/draft/changes` would do, saving nothing. `POST /v1/workflows/{id}/launch` launches after the preflight, checking the contacts given and everyone waiting in the Contacts box (`422 PREFLIGHT_BLOCKED` with `details.checks`; it starts contacts, so it needs **enrollments:write** too). `POST /v1/workflows/from-description` returns `destinations`, and the preflight's `SENDS_DATA_OUT` check lists the hosts a campaign sends contact data to (its webhook and HTTP steps and Always-on boxes). `POST /v1/workflows/{id}/enrollments` with `wait_for_launch: true` keeps contacts added to a campaign not launched yet in its Contacts box until the launch.
* **Email templates**: `GET` and `POST /v1/email-templates`, `GET`, `PATCH` and `DELETE /v1/email-templates/{id}`, `POST /v1/email-templates/{id}/duplicate`, `GET /v1/email-templates/{id}/versions` and `POST /v1/email-templates/{id}/versions/{version}/restore` (reads need **workflows:read**, changes **workflows:write**). `kind` is `blocks`, `html` or `text`. Send `base_version` (the version you read) with a change: if someone saved meanwhile, the answer is `422` `EDITED_ELSEWHERE`. `POST /v1/email-images` uploads a picture as `data_base64` (PNG, JPG, GIF or WebP, up to 1 MB; 25 MB per workspace) and returns its public `url`, `GET /v1/email-images` lists the workspace's pictures with `used_bytes` and `max_bytes`, and `DELETE /v1/email-images/{id}` deletes one (emails already sent with it show it broken). See [Email templates](/help/campaigns/email-templates.md).
* **An automatic answer waiting to go out**: `POST /v1/messages/{id}/send-now` sends it now (still checked again and within the account's limits, at most once) and `POST /v1/messages/{id}/cancel` stops it (it stays as the suggested reply). See [Use the inbox](/help/inbox/use-the-inbox.md#an-automatic-answer-waiting-to-go-out).
* **Text messages (SMS)** (coming soon: connecting Twilio answers `403 SMS_COMING_SOON` until texting opens): `/v1/sms`, `/v1/sms/twilio`, `/v1/sms-numbers/{id}` and `/v1/sms-opt-outs` connect and read your Twilio account, its numbers and the phones that texted STOP (**senders:read** and **senders:write** scopes; connecting, replacing or disconnecting the keys, and sending replies to Kerux need **admin**; the STOP list needs **leads:read**). See [Twilio (text messages)](/help/integrations/twilio-sms.md#api-and-mcp).
* **Sending rules and consent**: `GET /v1/workspace/sending-identity` (**workflows:read**) and `PUT /v1/workspace/sending-identity` (**admin**) read and set `company_name`, `postal_address` (required before campaign emails go out) and `footer_language`. `GET /v1/workspace/consent-default` (**workflows:read**) reads the workspace's **Contacts' consent** switch, and `PUT /v1/workspace/consent-default` (**admin**) with empty `channels` turns it off; turning it on is refused to API keys (`INSUFFICIENT_ROLE`): an owner or admin does it in **Settings > Sending rules**. `GET`, `POST /v1/leads/{id}/consent` and `DELETE /v1/leads/{id}/consent/{channel}` read, record (`channels` with `sms` and / or `whatsapp`, `attest: true`, optional `consent_text`) and withdraw a contact's consent (**leads:read** / **leads:write**; errors `CONSENT_NOT_CONFIRMED`, `CONSENT_NEEDS_PHONE`, `CONSENT_DO_NOT_CONTACT`). `POST /v1/leads/{id}/do-not-contact` (**leads:write**) stops every channel for a contact, and `POST /v1/leads/{id}/erase` (**admin**) is a deletion request. `GET`, `POST /v1/suppressions` and `DELETE /v1/suppressions/{id}` read, add to and remove from the do-not-contact list (**leads:read** / **leads:write**; only entries a teammate added in the app can be removed, else `SUPPRESSION_NOT_REMOVABLE`). See [Sending rules and consent](/help/campaigns/sending-rules-and-consent.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.