
# Deals API

A deal is one sales opportunity on your [Sales Pipeline](../deals/sales-pipeline.md) board — a title, a value, the stage it sits in, and optionally the contact it belongs to and the team member it is assigned to. This guide covers reading and managing deals over the API.

- **Base URL** — `https://api.youraiconnector.com/v1`
- **Authentication** — your API key (see [Authentication](authentication.md))
- **Errors & paging** — see [Errors & Pagination](errors-and-pagination.md)

All examples below show the `?apiKey=` query form in cURL and the `X-API-Key` header in JavaScript and Python — either works on every endpoint.

---

## The deal object

```json
{
  "id": "deal_abc123",
  "title": "Annual plan – Jane's Clinic",
  "description": "Asked for pricing on the annual plan.",
  "company": "Jane's Clinic",
  "notes": "Prefers a call after 5pm.",
  "stage": "stage-2",
  "pipeline_id": "default",
  "value": 4800,
  "probability": 60,
  "priority": "high",
  "status": "open",
  "source": "whatsapp",
  "position": 0,
  "contact_id": "ctc_9f8e7d",
  "assigned_to": "usr_4a2b",
  "tags": ["clinic"],
  "close_date": "2026-10-01T00:00:00.000Z",
  "created_at": "2026-09-05T09:12:00.000Z",
  "updated_at": "2026-09-05T09:40:00.000Z",
  "stage_entered_at": "2026-09-05T09:40:00.000Z"
}
```

`stage` is the id of one of your pipeline stages — read them with `GET /pipelines` (see [Pipelines](#pipelines) below). Stage ids are unique across all your pipelines, so `pipeline_id` is derived from the stage and is read-only: `default` is your main pipeline, and it is `null` when the stage no longer exists. Timestamps are ISO 8601.

A deal stores only the fields shown above. When you create or update one, any other field is refused with a `400` that names it, so nothing you send is silently lost. Keep extra data on the linked contact's custom fields.

---

## List deals

`GET /deals` — returns the deals on your account, newest first.

**Query parameters** (all optional): `stage` (a stage id), `pipeline_id` (only the deals on that pipeline), `contact_id`, `limit` (default 50, max 200), `cursor` (from the previous page's `next_cursor`).

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/deals?apiKey=YOUR_API_KEY&stage=stage-2&limit=50"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/deals?stage=stage-2&limit=50", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const { deals, next_cursor } = await res.json();
```

**Python**

```python
res = requests.get(
    "https://api.youraiconnector.com/v1/deals",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"stage": "stage-2", "limit": 50},
)
deals = res.json()["deals"]
```

**Response** (`200`)

```json
{ "success": true, "deals": [{ "id": "deal_abc123", "title": "Annual plan – Jane's Clinic", "...": "..." }], "next_cursor": null }
```

---

## Get a deal

`GET /deals/{dealId}`

```bash
curl "https://api.youraiconnector.com/v1/deals/deal_abc123?apiKey=YOUR_API_KEY"
```

**Response** (`200`)

```json
{ "success": true, "deal": { "id": "deal_abc123", "title": "Annual plan – Jane's Clinic", "...": "..." } }
```

A deal that does not exist, or belongs to another account, returns `404`.

---

## Create a deal

`POST /deals` — `title` and `stage` are required; `stage` must be one of your pipeline's stage ids.

```bash
curl -X POST "https://api.youraiconnector.com/v1/deals?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Annual plan – Jane'"'"'s Clinic", "stage": "stage-1", "value": 4800, "contact_id": "ctc_9f8e7d" }'
```

**Response** (`201`)

```json
{ "success": true, "deal_id": "deal_abc123" }
```

---

## Update a deal

`PUT /deals/{dealId}` — send only the fields you want to change (`title`, `description`, `notes`, `value`, `stage`, `status`, `priority`, `contact_id`, …). `value` must be a number.

```bash
curl -X PUT "https://api.youraiconnector.com/v1/deals/deal_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "value": 5200, "priority": "high" }'
```

**Response** (`200`): `{ "success": true, "deal_id": "deal_abc123" }`

---

## Move a deal to another stage

`POST /deals/{dealId}/move` — body `{ "new_stage_id": "stage-3", "new_position": 0 }`. Position `0` puts the card at the top of the column.

```bash
curl -X POST "https://api.youraiconnector.com/v1/deals/deal_abc123/move?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "new_stage_id": "stage-3", "new_position": 0 }'
```

**Response** (`200`): `{ "success": true, "deal_id": "deal_abc123", "stage": "stage-3", "position": 0 }`

---

## Delete a deal

`DELETE /deals/{dealId}` — removes the deal. **This cannot be undone.**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/deals/deal_abc123?apiKey=YOUR_API_KEY"
```

**Response** (`200`): `{ "success": true }`

---

## Pipelines

Every account has a main pipeline (`id: "default"`); you can add up to nine more, each with its own stages. Stage ids and trigger tags must be unique across all pipelines on the account, and each pipeline can mark one stage as its `won` and one as its `lost` outcome.

### List pipelines

`GET /pipelines` — every pipeline, main one first, each with its stages sorted by position.

```bash
curl "https://api.youraiconnector.com/v1/pipelines?apiKey=YOUR_API_KEY"
```

**Response** (`200`):

```json
{
  "success": true,
  "data": {
    "pipelines": [
      {
        "id": "default",
        "name": "Sales",
        "position": 0,
        "is_default": true,
        "settings": { "sync_stage_tags": false },
        "stages": [
          { "id": "stage-1", "name": "New lead", "color": "#3B82F6", "position": 0, "trigger_tag": null },
          { "id": "won", "name": "Won", "color": "#10B981", "position": 1, "outcome": "won" }
        ]
      }
    ]
  }
}
```

### Create a pipeline

`POST /pipelines` — body `{ "id"?, "name", "stages"?, "settings"? }`. Leave `id` out to have one generated. Each stage takes the same fields as the Stages dialog: `id`, `name`, `color`, `position`, optional `trigger_tag` (a tag path, `users/{uid}/tags/{tagId}`), `default_value`, `auto_move_after_days` + `auto_move_to_stage` (a stage of the same pipeline) and `outcome` (`"won"` or `"lost"`).

```bash
curl -X POST "https://api.youraiconnector.com/v1/pipelines?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "reviews",
    "name": "Review requests",
    "stages": [
      { "id": "rv_asked", "name": "Asked", "color": "#3B82F6", "position": 0 },
      { "id": "rv_done", "name": "Review posted", "color": "#10B981", "position": 1, "outcome": "won" }
    ]
  }'
```

**Response** (`201`): `{ "success": true, "data": { "pipeline": { ... } } }`. A stage id or trigger tag already used by another pipeline is a `400` naming that pipeline; an id that already exists is a `409`.

### Update a pipeline

`PUT /pipelines/{pipelineId}` — body `{ "name"?, "stages"?, "settings"? }`. `stages` replaces the whole list, like the Stages dialog. Use `default` for the main pipeline. The pipeline id itself cannot be changed.

### Delete a pipeline

`DELETE /pipelines/{pipelineId}` — removes an extra pipeline (the main one cannot be deleted). If deals still sit in its stages the call answers `409` with `deal_count`; add `?delete_deals=true` to delete those deals together with the pipeline. **This cannot be undone.**

**Response** (`204`).

---

## Next steps

- [Sales Pipeline](../deals/sales-pipeline.md) — how stages, stage tags, several pipelines and the board work.
- [Tasks API](tasks.md) — tasks can be linked to a deal with `deal_id`.
- [Automations](../automations/automations.md) — the **Find deals** and **Find deal** steps read the same data inside a workflow.
