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

# Quick Start - AP Agent

> Review and approve a bill using the conversational AP Agent

The Dolfin AP Agent lets your users review bills, correct OCR output, manage suppliers, and approve payments through natural language. You upload the bill document via the direct API, then drive the rest of the workflow through `POST /agent/ap`.

This guide walks through the same bill flow as the [AP Quick Start](/guides/quick-start-ap), but driven through the agent after the initial upload.

## Prerequisites

* A Dolfin **API key**
* An **organisation** already provisioned (see [Client Integration](/guides/client-integration))
* A valid **session token** or API key for authentication
* A bill **PDF or image** file to upload

<Note>
  All requests below require the `x-dolfin-api-key` and `x-dolfin-organisation-id` headers. See [Authentication](/guides/authentication) for details.
</Note>

## How the Agent Works

Every agent turn is a **single POST** to `/agent/ap` with just two body fields:

| Field            | Type             | Notes                                                                               |
| ---------------- | ---------------- | ----------------------------------------------------------------------------------- |
| `message`        | string, required | The user's message for this turn                                                    |
| `conversationId` | UUID, optional   | Omit on the first turn. Supply on every subsequent turn to continue the same thread |

You do not send the full message history — Dolfin persists the conversation server-side and the agent loads it automatically from `conversationId`.

### Response format

The response is a `text/event-stream` (SSE). Read events as they arrive:

| Event          | Payload                       | When                                                       |
| -------------- | ----------------------------- | ---------------------------------------------------------- |
| `conversation` | `{ "id": "uuid" }`            | Fired **once**, on the first turn only, before any content |
| *(unnamed)*    | `{ "content": "text chunk" }` | Streamed assistant text, one chunk per data frame          |
| `action`       | Tool action object            | When the agent runs a tool (e.g. patched a bill)           |
| *(unnamed)*    | `{ "done": true }`            | Final frame — stream is complete                           |

On error, the final frame is `{ "error": "message", "done": true }`.

### Persisting `conversationId`

On the first request, **read the `conversation` SSE event** and save its `data.id`. Echo that UUID back as `conversationId` on every follow-up POST to continue the same conversation. See the [AR Agent guide](/guides/quick-start-ar-agent) for an SSE parser example.

<Note>
  Once the first response returns a `conversationId`, the agent type is **frozen** on that conversation. Sending a request to `POST /agent/ar` with an AP conversation's `conversationId` (or vice versa) returns `409 Conflict`.
</Note>

### Conversation scoping

Conversations are scoped by:

* **Organisation** — always. A conversation belongs to the organisation in `x-dolfin-organisation-id` and can't be read or continued from another organisation.
* **User** — only when the call is authenticated with a Bearer JWT. API-key calls have no user binding, so any caller with the same API key and organisation can continue an API-key conversation.

## Overview

<Steps>
  <Step title="Upload the bill (direct API)">
    Bill uploads are multipart file transfers, so Step 1 uses `POST /bills` directly. After this, everything else runs through the agent.
  </Step>

  <Step title="Ask the agent to review the bill">
    Start a conversation by sending the bill's ID to the agent. It loads the OCR output and walks you through it.
  </Step>

  <Step title="Correct any fields (optional)">
    Tell the agent what's wrong in natural language; it calls the `patchBill` tool to save your edits.
  </Step>

  <Step title="Submit for review and approve">
    The agent submits the bill for approval and, once approved, moves it to `Approved`.
  </Step>
</Steps>

***

## Step 1: Upload the Bill (Direct API)

Bill uploads happen via `POST /bills` as `multipart/form-data`. OCR runs asynchronously.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.dolfinai.co/bills \
    -H "x-dolfin-api-key: dol_live_abc123" \
    -H "x-dolfin-organisation-id: 9a658587-fe02-402e-b1ac-bfaf53274ef8" \
    -F "file=@./acme-invoice-2025-01.pdf"
  ```

  ```javascript Node.js theme={null}
  import fs from 'node:fs';

  const form = new FormData();
  form.append(
    'file',
    new Blob([fs.readFileSync('./acme-invoice-2025-01.pdf')], {
      type: 'application/pdf',
    }),
    'acme-invoice-2025-01.pdf',
  );

  const { id: billId } = await fetch('https://api.dolfinai.co/bills', {
    method: 'POST',
    headers: {
      'x-dolfin-api-key': 'dol_live_abc123',
      'x-dolfin-organisation-id': '9a658587-fe02-402e-b1ac-bfaf53274ef8',
    },
    body: form,
  }).then(r => r.json());
  ```
</CodeGroup>

**Response (`202 Accepted`):**

```json theme={null}
{
  "id": "b1234567-abcd-ef01-2345-6789abcdef01",
  "state": "OcrProcessing"
}
```

<Note>
  Save the bill `id` — you'll hand it to the agent next. Wait for the state to move to `PendingReview` (poll `GET /bills/{id}` or subscribe to the `/hubs/bills` SignalR hub) before talking to the agent.
</Note>

***

## Step 2: Ask the Agent to Review the Bill

This is the first turn of the conversation, so **omit `conversationId`**. Give the agent the bill ID and ask it to walk you through the OCR output.

<CodeGroup>
  ```bash cURL theme={null}
  curl -N -X POST https://api.dolfinai.co/agent/ap \
    -H "x-dolfin-api-key: dol_live_abc123" \
    -H "x-dolfin-organisation-id: 9a658587-fe02-402e-b1ac-bfaf53274ef8" \
    -H "Content-Type: application/json" \
    -H "Accept: text/event-stream" \
    -d '{
      "message": "I just uploaded bill b1234567-abcd-ef01-2345-6789abcdef01 — walk me through what OCR extracted."
    }'
  ```

  ```javascript Node.js theme={null}
  const { conversationId } = await chat(
    'I just uploaded bill b1234567-abcd-ef01-2345-6789abcdef01 — walk me through what OCR extracted.',
  );
  // Persist conversationId — you need it for every subsequent turn.
  ```
</CodeGroup>

**SSE stream (excerpt):**

```
event: conversation
data: {"id":"a7e1f3b2-9c0d-4e8a-b6f5-3d2e4f7c8a91"}

event: action
data: {"type":"bill.retrieved","billId":"b1234567-abcd-ef01-2345-6789abcdef01", ...}

data: {"content":"Here's what OCR extracted from that bill:"}

data: {"content":"\n\n- **Supplier:** Acme Supplies Ltd"}

...

data: {"done":true}
```

The agent replies with a summary:

> Here's what OCR extracted from that bill:
>
> * **Supplier:** Acme Supplies Ltd ([billing@acme.co.uk](mailto:billing@acme.co.uk))
> * **Invoice number:** ACM-2025-0142
> * **Invoice date:** 15 Jan 2025
> * **Due date:** 14 Feb 2025
> * **Currency:** GBP
> * **Subtotal:** £480.00
> * **Tax:** £96.00
> * **Total:** £576.00
> * **Line items:**
>   1. Bulk stationery order × 10 @ £48.00 (20% tax)
>
> OCR confidence is 0.94. Anything you'd like me to correct before we submit for review?

***

## Step 3: Correct Any Fields (Optional)

Tell the agent what's off. It'll call the `patchBill` tool for you — no need to craft a patch payload by hand.

<CodeGroup>
  ```bash cURL theme={null}
  curl -N -X POST https://api.dolfinai.co/agent/ap \
    -H "x-dolfin-api-key: dol_live_abc123" \
    -H "x-dolfin-organisation-id: 9a658587-fe02-402e-b1ac-bfaf53274ef8" \
    -H "Content-Type: application/json" \
    -H "Accept: text/event-stream" \
    -d '{
      "conversationId": "a7e1f3b2-9c0d-4e8a-b6f5-3d2e4f7c8a91",
      "message": "Invoice number should be ACM-2025-00142, and add bank details: account 12345678, sort code 20-00-00, GB/GBP."
    }'
  ```

  ```javascript Node.js theme={null}
  await chat(
    'Invoice number should be ACM-2025-00142, and add bank details: account 12345678, sort code 20-00-00, GB/GBP.',
    conversationId,
  );
  ```
</CodeGroup>

The agent calls `patchBill`, emits an `action` event, and confirms:

> Done — I've updated:
>
> * **Invoice number:** ACM-2025-00142
> * **Bank details:** Acme Supplies Ltd · Account 12345678 · Sort 20-00-00 · GB / GBP
>
> Ready to submit for review?

<Note>
  The agent can only edit bills in `PendingReview` — matching the direct-API rule. If OCR is still running, ask again in a few seconds.
</Note>

***

## Step 4: Submit for Review and Approve

When the data looks right, have the agent submit the bill and then approve it.

### Submit for review

<CodeGroup>
  ```bash cURL theme={null}
  curl -N -X POST https://api.dolfinai.co/agent/ap \
    -H "x-dolfin-api-key: dol_live_abc123" \
    -H "x-dolfin-organisation-id: 9a658587-fe02-402e-b1ac-bfaf53274ef8" \
    -H "Content-Type: application/json" \
    -H "Accept: text/event-stream" \
    -d '{
      "conversationId": "a7e1f3b2-9c0d-4e8a-b6f5-3d2e4f7c8a91",
      "message": "Yes, submit for review."
    }'
  ```

  ```javascript Node.js theme={null}
  await chat('Yes, submit for review.', conversationId);
  ```
</CodeGroup>

The agent confirms and calls `submitBillReview`:

> Submitted. The bill is now in **NeedsApproval** — Acme Supplies Ltd is linked as the supplier. Ready for you to approve?

<Note>
  **Supplier auto-resolution.** When the agent submits a bill, Dolfin resolves the supplier in this order: an explicit `supplierId` you set → an existing supplier on the organisation whose tax ID or email matches → a **new supplier created from the OCR data** (requires at minimum `supplierName` and `supplierEmail`). If the agent doesn't have enough data to resolve or create a supplier, submit-review returns `400 Bad Request`.
</Note>

### Approve

<CodeGroup>
  ```bash cURL theme={null}
  curl -N -X POST https://api.dolfinai.co/agent/ap \
    -H "x-dolfin-api-key: dol_live_abc123" \
    -H "x-dolfin-organisation-id: 9a658587-fe02-402e-b1ac-bfaf53274ef8" \
    -H "Content-Type: application/json" \
    -H "Accept: text/event-stream" \
    -d '{
      "conversationId": "a7e1f3b2-9c0d-4e8a-b6f5-3d2e4f7c8a91",
      "message": "Approve it."
    }'
  ```

  ```javascript Node.js theme={null}
  await chat('Approve it.', conversationId);
  ```
</CodeGroup>

The agent runs `approveBill` and confirms:

> Approved. The bill is now in **Approved** and ready for payment scheduling. Want me to schedule the payment now?

The agent can also schedule the payment (`schedulePayment`) and confirm payment (`confirmPaid`) via further turns.

<Note>
  **Rejecting a bill.** The AP agent can't reject bills — rejection is a direct-API-only call via `POST /bills/{id}/reject`. Use the [AP Quick Start](/guides/quick-start-ap#step-4-approve-or-reject) for that flow.
</Note>

***

## Full Conversation Flow

| Turn | `conversationId` sent? | User message                                                        |
| ---- | ---------------------- | ------------------------------------------------------------------- |
| 1    | No (first turn)        | "I just uploaded bill `{id}` — walk me through what OCR extracted." |
| 2    | Yes                    | "Invoice number should be ACM-2025-00142, and add bank details..."  |
| 3    | Yes                    | "Yes, submit for review."                                           |
| 4    | Yes                    | "Approve it."                                                       |

## Managing Conversations

| Endpoint                                 | Purpose                                |
| ---------------------------------------- | -------------------------------------- |
| `GET /agent/conversations?agentType=Ap`  | List the caller's AP conversations     |
| `GET /agent/conversations/{id}`          | Get metadata for a single conversation |
| `GET /agent/conversations/{id}/messages` | Paged message history                  |
| `PATCH /agent/conversations/{id}`        | Rename a conversation                  |
| `DELETE /agent/conversations/{id}`       | Delete a conversation and its messages |

## Agent vs Direct API

|                      | Direct API                               | AP Agent                                |
| -------------------- | ---------------------------------------- | --------------------------------------- |
| **Upload**           | `POST /bills` (multipart)                | Same — agent doesn't handle file upload |
| **Review / patch**   | Craft a `PATCH /bills/{id}` body by hand | Describe the changes in plain English   |
| **Submit / approve** | Sequential endpoint calls                | Single-turn natural language requests   |
| **Reject**           | `POST /bills/{id}/reject`                | Direct API only                         |

## Next Steps

<CardGroup cols={2}>
  <Card title="AP Quick Start (Direct API)" icon="bolt" href="/guides/quick-start-ap">
    See the same flow using direct API calls, including rejection and re-open.
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Explore the full Bills API, including payment scheduling and file URLs.
  </Card>
</CardGroup>
