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

# Working with leads

> Read your pipeline, move a lead between stages, and log follow-ups.

The CRM endpoints live on a different host from messaging — `https://api.linkiasoft.com/api`
— but take the **same API key**. Both headers from [Authentication](/authentication) apply
unchanged.

<Warning>
  Read [pipeline stages](#stages-are-keys-not-names) before writing anything that moves a
  lead. Getting this wrong is the most common way these integrations break.
</Warning>

## Stages are keys, not names

A lead's stage is stored in its `status` field as a **stage key** — a stable slug. What you
see on the board is the stage's `name`, which anyone can rename at any time.

```bash theme={null}
curl https://api.linkiasoft.com/api/v1/lead-stages \
  -H "x-api-key: $LINKIA_API_KEY" \
  -H "X-Tenant-Id: $LINKIA_TENANT_ID"
```

```json theme={null}
[
  { "key": "new",       "name": "New",       "position": 0, "is_default": true },
  { "key": "qualified", "name": "Qualified", "position": 1 },
  { "key": "won",       "name": "Closed Won", "position": 2, "is_won": true },
  { "key": "lost",      "name": "Closed Lost", "position": 3, "is_lost": true }
]
```

Two rules that follow from this:

* **Send `key`, never `name`.** `status: "qualified"` moves a lead; `status: "Qualified"` does not.
* **Judge outcomes by `is_won` / `is_lost`, never by comparing the key to `"won"`.** Tenants
  rename and re-key their stages. A pipeline with `closed_deal` instead of `won` is normal,
  and code matching the literal string silently reports zero conversions.

Fetch the stage list once at startup and cache it, rather than hard-coding keys.

## List leads in a stage

```bash theme={null}
curl "https://api.linkiasoft.com/api/v1/leads?status=qualified&limit=20" \
  -H "x-api-key: $LINKIA_API_KEY" \
  -H "X-Tenant-Id: $LINKIA_TENANT_ID"
```

```json theme={null}
{
  "data": [
    {
      "id": "5e6f7a8b-9c0d-4e1f-2a3b-4c5d6e7f8a9b",
      "name": "Ahmed Hassan",
      "company_name": "Acme Trading",
      "phone": "201001234567",
      "status": "qualified",
      "value": 25000,
      "currency": "EGP",
      "assigned_to_user_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
    }
  ],
  "total": 42,
  "page": 1,
  "limit": 20,
  "totalPages": 3,
  "hasMore": true
}
```

<Note>
  When you are reading a whole board, page with `offset` rather than `page`. A lead moving
  out of a stage mid-read shifts a page-numbered window and makes you skip a row. `offset`
  wins over `page` when both are sent.
</Note>

For column counts without pulling every lead, use
[`GET /v1/leads/stage-counts`](/api-reference/leads/count-leads-per-stage).

## Move a lead to another stage

Moving through the pipeline is just a `status` update:

```bash theme={null}
curl -X PATCH https://api.linkiasoft.com/api/v1/leads/$LEAD_ID \
  -H "x-api-key: $LINKIA_API_KEY" \
  -H "X-Tenant-Id: $LINKIA_TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{ "status": "won" }'
```

Send only the fields you are changing. The same endpoint updates deal details:

```json theme={null}
{ "value": 25000, "currency": "EGP", "priority": "high", "expected_close_date": "2026-09-30" }
```

To hand a lead to a different rep, use
[`PATCH /v1/leads/{id}/reassign`](/api-reference/leads/reassign-a-lead-to-another-user)
rather than setting `assigned_to_user_id` directly.

## Mark a follow-up

There is **no follow-up flag on the lead**. A follow-up is an activity whose `outcome` is
`follow_up_needed`:

```bash theme={null}
curl -X POST https://api.linkiasoft.com/api/activities/lead/$LEAD_ID \
  -H "x-api-key: $LINKIA_API_KEY" \
  -H "X-Tenant-Id: $LINKIA_TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "activity_type": "call",
    "subject": "Discussed pricing",
    "activity_date": "2026-07-25T10:30:00Z",
    "duration_minutes": 15,
    "outcome": "follow_up_needed"
  }'
```

`activity_type` is one of `call`, `email`, `meeting`, `note`, `task`, `deal`,
`support_ticket`, `sms`. `outcome` is one of `successful`, `follow_up_needed`, `no_answer`,
`completed`, `cancelled`.

<Warning>
  Activity paths have **no `/v1`** prefix — `/api/activities/...`, not `/api/v1/activities/...`.
  The same is true of contacts. Leads and stages do use `/v1`.
</Warning>

## Contacts at a company

A **customer** is the company; **contacts** are the people at it.

```bash theme={null}
curl https://api.linkiasoft.com/api/contacts/customer/$CUSTOMER_ID \
  -H "x-api-key: $LINKIA_API_KEY" \
  -H "X-Tenant-Id: $LINKIA_TENANT_ID"
```

Each contact carries `mobile` alongside `phone` — prefer `mobile` when you intend to message
them on WhatsApp, and note `is_primary` marks the main point of contact.

## Scopes

| To do this                              | The key needs      |
| --------------------------------------- | ------------------ |
| Read leads, stages, activities          | `leads:read`       |
| Create a lead                           | `leads:create`     |
| Move a stage, reassign, log an activity | `leads:update`     |
| Delete a lead                           | `leads:delete`     |
| Read customers and contacts             | `customers:read`   |
| Create or edit a contact                | `customers:update` |

Contacts and activities are governed by the `customers` and `leads` scopes — there is no
`contacts` or `activities` scope. A key missing the scope gets `403` naming what it lacks.

## Putting it together

The obvious pairing with the Messaging API: when a lead reaches a stage, message them.

<Steps>
  <Step title="Detect the change">
    Poll [`GET /v1/leads`](/api-reference/leads/list-leads) filtered to the stage, or trigger
    from whatever moved the lead in the first place.
  </Step>

  <Step title="Get a number">
    The lead's own `phone`, or the primary contact's `mobile` from
    [`GET /contacts/customer/{customerId}`](/api-reference/contacts/list-contacts-for-a-company).
    Normalize to E.164 with no `+` — see [the quickstart](/quickstart).
  </Step>

  <Step title="Send a template">
    [`POST /v1/conversations/send`](/api-reference/conversations/send-a-message) on
    `social.linkiasoft.com`. Outside the 24-hour window it must be a template.
  </Step>

  <Step title="Log what you did">
    Post an activity against the lead so the rep sees it in the timeline.
  </Step>
</Steps>
