# Iblai API Crm

> Manage an ibl.ai organization's CRM via the platform API — people, organizations, pipelines and stages, lead sources, deals (with stage-move/won/lost actions), activities, and tags. Org-wide sales/relationship management. Use for lead capture, pipelines, and deal flow.

- Skill: `iblai/iblai-api-crm` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add iblai/iblai-api-crm`
- Raw SKILL.md: https://api.skillmd.com/api/skills/iblai/iblai-api-crm/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: iblai (https://skillmd.com/u/iblai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/iblai/iblai-api-crm

---


# iblai-api-crm

Manage an organization's **CRM** from the API: people and organizations,
pipelines and their stages, lead sources, deals (with stage-move / won / lost
actions), activities, and tags — all the sales and relationship-management
records for one organization. Use for lead capture, pipelines, and deal flow.

## Auth & conventions

- **Base URL:** `https://api.iblai.app/dm` — CRM is a Data Manager (DM)
  endpoint, so the **`/dm` prefix is required**; the `/api/crm/...` paths below
  are appended to it (e.g. `https://api.iblai.app/dm/api/crm/persons/`).
- **Header:** `Authorization: Api-Token $IBLAI_API_KEY` on every request. (The CRM
  developer docs phrase this as `Authorization: Token <key>` — it is the same
  Platform API Token.)
- **Scope:** Platform-scoped. Every record belongs to the organization resolved
  from the token, so there is **no `{org}` in the path**.
- **Ids:** integers for every resource **except** Person and Organization, whose
  ids are **UUID** strings.
- Not connected yet? Run **`/iblai-api-login`** first to populate `IBLAI_ORG`,
  `IBLAI_USERNAME`, and `IBLAI_API_KEY`.

## Resources

| Resource     | Path                                      | Id                              |
| ------------ | ----------------------------------------- | ------------------------------- |
| Person       | `/api/crm/persons/`                       | UUID                            |
| Organization | `/api/crm/organizations/`                 | UUID                            |
| Pipeline     | `/api/crm/pipelines/`                      | int                             |
| Stage        | `/api/crm/pipelines/{pipeline_id}/stages/` | int (nested under a pipeline)  |
| Lead Source  | `/api/crm/lead-sources/`                  | int                             |
| Deal         | `/api/crm/deals/`                         | int                             |
| Activity     | `/api/crm/activities/`                    | int                             |
| Tag          | `/api/crm/tags/`                          | int                             |

Each resource supports standard REST: **GET** (list), **POST** (create),
**GET** `{id}` (read), **PATCH** `{id}` (update), **DELETE** `{id}` (delete).
DELETE is destructive — confirm with the user first.

### Useful filters

- `owner={user_id}` — "my pipeline" / "my accounts".
- `owner__isnull=true` — unowned records.
- `?is_default=true` on pipelines — the seeded pipeline; its response embeds its
  stages inline.

## Pagination

List `GET`s are page-numbered. Navigate with `?page={n}` and override the page
size with `?page_size={n}` (default `50`). The list envelope is:

```json
{ "count": 137, "next_page": 3, "previous_page": 1, "results": [ /* … */ ] }
```

`next_page` / `previous_page` are page **numbers** (or `null` at the ends), not
URLs — a detail different from stock DRF pagination.

## Reads

### Person

- **GET** `/api/crm/persons/` — list people.
- **GET** `/api/crm/persons/{id}/` — read a person.

### Organization

- **GET** `/api/crm/organizations/` — list organizations.
- **GET** `/api/crm/organizations/{id}/` — read an organization.

### Pipeline

- **GET** `/api/crm/pipelines/` — list pipelines.
- **GET** `/api/crm/pipelines/{id}/` — read a pipeline.

### Stage

- **GET** `/api/crm/pipelines/{pipeline_id}/stages/` — list a pipeline's stages.
- **GET** `/api/crm/pipelines/{pipeline_id}/stages/{id}/` — read a stage.

### Lead Source

- **GET** `/api/crm/lead-sources/` — list lead sources.
- **GET** `/api/crm/lead-sources/{id}/` — read a lead source.

### Deal

- **GET** `/api/crm/deals/` — list deals.
- **GET** `/api/crm/deals/{id}/` — read a deal.

### Activity

- **GET** `/api/crm/activities/` — list activities.
- **GET** `/api/crm/activities/{id}/` — read an activity.

### Tag

- **GET** `/api/crm/tags/` — list tags.
- **GET** `/api/crm/tags/{id}/` — read a tag.

## Writes

### Person

- **POST** `/api/crm/persons/` — create a person.
- **PATCH** `/api/crm/persons/{id}/` — update a person.
- **DELETE** `/api/crm/persons/{id}/` — delete a person. Confirm with the user first.

**Person actions:**

- **POST** `/api/crm/persons/merge/` — merge duplicates into one: body
  `{ "primary_id": UUID, "duplicate_ids": [UUID, …] }`; reparents the duplicates'
  deals / activities / tags onto the primary. Destructive — confirm first.
- **POST** `/api/crm/persons/{id}/invite/` — email an invitation to the person's
  `primary_email`. Body (all optional): `is_admin` (bool), `is_staff` (bool),
  `enrollment_config` (object, forwarded to auto-enroll the invitee),
  `redirect_to` (url). Success returns the `invitation_id`; `409` if an active
  invitation already exists for that email (the response carries the existing
  `invitation_id`, so you can track / resend it), `422` if the person is already
  linked to a platform user. Sends outward — confirm with the user first.
- **POST** `/api/crm/persons/{id}/link-user/` — link the CRM person to an existing
  platform user: body `{ "user_id": int }` (required; the user must already be an
  active member of your org, else `403`). Sets `platform_user`.

### Organization

- **POST** `/api/crm/organizations/` — create an organization.
- **PATCH** `/api/crm/organizations/{id}/` — update an organization.
- **DELETE** `/api/crm/organizations/{id}/` — delete an organization. Confirm with the user first.

### Pipeline

- **POST** `/api/crm/pipelines/` — create a pipeline.
- **PATCH** `/api/crm/pipelines/{id}/` — update a pipeline.
- **DELETE** `/api/crm/pipelines/{id}/` — delete a pipeline. Confirm with the user first.

### Stage

- **POST** `/api/crm/pipelines/{pipeline_id}/stages/` — create a stage.
- **PATCH** `/api/crm/pipelines/{pipeline_id}/stages/{id}/` — update a stage.
- **DELETE** `/api/crm/pipelines/{pipeline_id}/stages/{id}/` — delete a stage. Confirm with the user first.

### Lead Source

- **POST** `/api/crm/lead-sources/` — create a lead source.
- **PATCH** `/api/crm/lead-sources/{id}/` — update a lead source.
- **DELETE** `/api/crm/lead-sources/{id}/` — delete a lead source. Confirm with the user first.

### Deal

- **POST** `/api/crm/deals/` — create a deal.
- **PATCH** `/api/crm/deals/{id}/` — update a deal.
- **DELETE** `/api/crm/deals/{id}/` — delete a deal. Confirm with the user first.

**Deal actions** (the canonical way to transition deals):

- **PATCH** `/api/crm/deals/{id}/` — reposition `stage` within a pipeline (allowed).
- **POST** `/api/crm/deals/{id}/move-stage/` — transition stage; body accepts
  `stage_code` (preferred) or `stage_id`.
- **POST** `/api/crm/deals/{id}/won/` — close the deal as won. Body optional:
  `stage_code` to target a specific `is_won` stage (defaults to the pipeline's
  first `is_won` stage by `sort_order`).
- **POST** `/api/crm/deals/{id}/lost/` — close the deal as lost. Body **requires**
  a non-empty `lost_reason` (≤255; `400` if missing); optional `stage_code`
  (defaults to the first `is_lost` stage).

### Activity

- **POST** `/api/crm/activities/` — create an activity.
- **PATCH** `/api/crm/activities/{id}/` — update an activity.
- **DELETE** `/api/crm/activities/{id}/` — delete an activity. Confirm with the user first.
- **POST** `/api/crm/activities/{id}/done/` — mark the activity done (stamps `done_at`; see `is_done`/`done_at` in the schema).

### Tag

- **POST** `/api/crm/tags/` — create a tag.
- **PATCH** `/api/crm/tags/{id}/` — update a tag.
- **DELETE** `/api/crm/tags/{id}/` — delete a tag. Cascades — removes every
  assignment on every person / organization / deal. Confirm with the user first.

**Attach / detach on a host** (`{host}` = `persons` | `organizations` | `deals`;
both need `Ibl.CRM/Tags/write`, a **separate** RBAC bucket from host-write — see
[`references/workflows.md`](references/workflows.md) § Tagging):

- **POST** `/api/crm/{host}/{id}/tags/` — attach an existing tag to a host record:
  body `{ "tag_id": int }` (`≥ 1`). Returns `201` with `{ assignment_id, tag }`;
  `409` if already attached (idempotent — carries the existing `assignment_id`, so
  treat it as success); `404` if the tag isn't in your org.
- **DELETE** `/api/crm/{host}/{id}/tags/{tag_id}/` — detach a tag (removes the
  assignment row, not the tag). `204` on success; `404` if it wasn't attached. Not
  idempotent — a repeat DELETE returns `404`; treat that as a no-op success.

## Example

Create a person:

```bash
curl -X POST \
  "https://api.iblai.app/dm/api/crm/persons/" \
  -H "Authorization: Api-Token $IBLAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ada Lovelace",
    "primary_email": "ada@example.com",
    "lifecycle_stage": "lead"
  }'
```

## Notes

- CRM is served via the DM gateway at `https://api.iblai.app/dm/api/crm/`
  (equivalent to the legacy `https://platform.iblai.app/api/crm/` host; skills
  standardize on the `api.iblai.app/dm` form).
- Lifecycle stages are `lead | qualified | opportunity | customer | churned`.
- A CRM Person auto-links to a Platform user when a signup matches by email.
- Stages are nested under a pipeline; deal transitions go through the deal
  actions (`move-stage/`, `won/`, `lost/`) rather than ad-hoc edits.
- Every read is scoped to your org (the token's `platform`), so a record in
  another org returns `404`, not `403` — existence is never leaked. Treat `404`
  as "not found **or** not visible to you".

## Schema

Field-level request/response shape (**Mode** `req`/`opt`/`ro`) for every resource, plus the
enums and the confirmed filter query params, live in a reference file to keep this skill
scannable: **[`references/schema.md`](references/schema.md)**. Read it when you need exact
field names, types, defaults, or which filters a list `GET` accepts.

## Reference material

The endpoints and field schemas above are the authoritative, code-verified surface. These
companion files (ported from the CRM developer guide) preserve the surrounding concepts,
workflows, and guidance — read them for depth, not for endpoint truth:

- **[`references/schema.md`](references/schema.md)** — field-level request/response shape (**Mode** `req`/`opt`/`ro`), enums, and the confirmed filter query params for every resource.
- **[`references/guide.md`](references/guide.md)** — concepts & data model: system overview, write side-effects, the deal-status state machine, auth/security notes, the object graph, the resource map, and per-resource deletion/cascade behavior.
- **[`references/quickstart.md`](references/quickstart.md)** — end-to-end worked walkthrough: capture a lead, find the seeded pipeline, open a deal, move it through stages, and close it won.
- **[`references/workflows.md`](references/workflows.md)** — subsystem lifecycles: person onboarding (link / invite / merge / auto-link), deal lifecycle, activity timeline & auto-records, tagging, and CRM notifications (cross-refs `/iblai-api-notification`).
- **[`references/operations.md`](references/operations.md)** — filtering & pagination, the RBAC role/permission matrix (cross-refs `/iblai-api-rbac`), the error reference, and best practices.

