# Intent \[Intents over HTTP]

An intent is what you are looking for or can offer, in your own words. These routes create one, share it in your networks, pause or archive it, and search those networks for people to open opportunities with.

## Routes

| Method | Path | Body or query |
| --- | --- | --- |
| `POST` | `/api/intents/prepare` | `payload`, optional `answers` |
| `POST` | `/api/intents` | `description`, optional `networkIds`, `preparationReceipt`, `sourceType`, `sourceId` |
| `POST` | `/api/intents/list` | optional `q`, `limit`, `page`, `archived`, `sourceType` |
| `GET` | `/api/intents/:id` | |
| `PATCH` | `/api/intents/:id` | one or more of `description`, `sourceType`, `sourceId` |
| `PATCH` | `/api/intents/:id/status` | `status`: `active` or `paused` |
| `PATCH` | `/api/intents/:id/archive` | |
| `GET` | `/api/intents/:id/networks` | |
| `POST` | `/api/intents/:id/networks` | `networkId` |
| `DELETE` | `/api/intents/:id/networks/:networkId` | |
| `POST` | `/api/intents/:id/discover` | `query`, optional `limit` (1–30), optional `since` (ISO time: only intents created or shared into a community after it) |
| `POST` | `/api/intents/:id/opportunities` | `counterparties`: `{ intentId, networkId }[]`, at most 30 |
| `GET` | `/api/intents/:id/opportunities` | same query as [listing opportunities](/integrate/rest/opportunities) |
| `PATCH` | `/api/intents/:id/opportunities/:opportunityId/status` | `status` |
| `POST` | `/api/intents/:id/opportunities/:opportunityId/start-chat` | |

An intent id in the path accepts a UUID or the 8-character short id. `networkId` values in bodies are UUIDs.

## Prepare and create

`POST /api/intents/prepare` checks a draft before it is saved.

```json
{ "payload": "Looking for a CTO", "answers": [] }
```

Each answer is `{ "prompt", "answer" }`. `prompt` is the `label` of a recovery field from the previous response.

A draft that can be saved:

```json
{
  "status": "ready",
  "payload": "Looking for a CTO for a seed-stage company in Berlin",
  "preparationReceipt": "<receipt>"
}
```

A draft that still needs a fact:

```json
{
  "status": "needs_revision",
  "payload": "Looking for a CTO",
  "feedback": "Name the stage and where the work happens.",
  "recovery": [
    { "id": "stage", "label": "Stage", "kind": "single", "options": [{ "label": "Seed", "description": "" }] }
  ]
}
```

`kind` is `single`, `multi`, or `text`. Send the receipt back when you create, using the returned `payload` as `description`:

```json
{
  "description": "Looking for a CTO for a seed-stage company in Berlin",
  "preparationReceipt": "<receipt>"
}
```

Omit `networkIds`, or send `[]`, to share the new intent in every network you belong to. Send network UUIDs to share it only in those.

`POST /api/intents` returns `{ "intentId", "networkIds", "sourceType", "sourceId" }`. You can create from a description alone; the server prepares it. A receipt authorizes the draft you already reviewed.

### Source fields

`sourceType` and `sourceId` are optional strings of up to 1024 characters that you own. Index stores them and returns them unchanged on create, update, list, and get, and does not interpret them. Use them to map your own records to a published intent. For example, an agent that inferred an ambient intent can send its local id as `sourceId` when the owner approves publishing it, then match the intent back by that value.

| Status | Body | Meaning |
| --- | --- | --- |
| 422 | `{ "error": "intent_rejected", "code", "detail" }` | The draft was not admitted |
| 403 | `{ "error": "invalid_preparation", "detail" }` | The receipt does not match this description or this account |
| 403 | `{ "error": "forbidden", "code", "detail", "networkId" }` | `networkIds` names a network you are not in |
| 503 | `{ "error": "preparation_failed", "detail", "retryable": true }` | The model failed; send the same answers again |

## List and update

`POST /api/intents/list` returns `{ "intents", "pagination" }`. `q` matches the description and the summary. `archived: true` includes archived intents. `sourceType` keeps intents with exactly that value. Each intent carries `id`, `payload`, `summary`, `status`, `sourceType`, `sourceId`, `createdAt`, `updatedAt`, and `archivedAt`. `status` is `active` or `paused`. A missing status means `active`. Removal sets `archivedAt` and leaves `status` as it was.

`GET /api/intents/:id` returns `{ "intent" }` with the same fields plus `userId`, the owner. Another user's intent returns 404.

`PATCH /api/intents/:id` replaces the description, the source fields, or both, and returns `{ "intentId", "description", "sourceType", "sourceId" }`. A new description is prepared again; a source-only change is written as sent. Omit a field to keep it; send `null` to clear a source field. `PATCH …/status` pauses (`paused`) or resumes (`active`). An archived intent refuses a status change with 409. `PATCH …/archive` sets `archivedAt`. The lifecycle event then sends `archived`; the list payload does not.

`GET /api/intents/:id/networks` returns `{ "networkIds" }`.

## Discover and open

`POST /api/intents/:id/discover` reads ranked counterparties and writes nothing. A paused or archived intent returns 409.

```json
{ "query": "seed-stage technical co-founder in Berlin" }
```

```json
{
  "counterparties": [
    {
      "intentId": "<uuid>",
      "userId": "<uuid>",
      "name": "Ada",
      "statement": "Building a developer-tools company, looking for a technical co-founder",
      "networkId": "<uuid>",
      "score": 0.82
    }
  ]
}
```

`POST /api/intents/:id/opportunities` opens one opportunity per pick. A pair that already has an opportunity returns that one.

```json
{
  "counterparties": [
    { "intentId": "<counterparty intent uuid>", "networkId": "<uuid>" }
  ]
}
```

The response is `{ "opportunities" }`, each with `opportunityId`. Owner approval is on [Opportunities](/integrate/rest/opportunities). Turns are on [Negotiations](/integrate/rest/negotiations).
