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

# Webhook payloads

> Reference the request headers, common envelope, and event-family payloads.

Fluent sends an HTTPS `POST` with a JSON body for every delivery attempt.

```http theme={null}
POST /hooks/fluent HTTP/1.1
Content-Type: application/json
User-Agent: Fluent-Webhooks/1
Fluent-Event-Id: 0c92ee55-2b2a-44ec-bdcc-91c73d48367c
Fluent-Event-Type: document.processed
Fluent-Delivery-Id: 7a517125-0424-48d0-acd0-cf1ca82c8657
Fluent-Attempt: 1
Fluent-Signature: t=1786461631,v1=9c1f...
```

## Request headers

| Header               | Meaning                                                                  |
| -------------------- | ------------------------------------------------------------------------ |
| `Content-Type`       | Always `application/json`.                                               |
| `User-Agent`         | `Fluent-Webhooks/1`.                                                     |
| `Fluent-Event-Id`    | Stable event ID and idempotency key. Equal to `body.id`.                 |
| `Fluent-Event-Type`  | Event name. Equal to `body.type`.                                        |
| `Fluent-Delivery-Id` | Delivery to this specific endpoint. Stable across its automatic retries. |
| `Fluent-Attempt`     | Current automatic attempt number, beginning at `1`.                      |
| `Fluent-Signature`   | Timestamped HMAC-SHA256 signature of the raw body.                       |

Use the event ID, not the delivery ID, for business idempotency. The same event can produce distinct delivery IDs when more than one endpoint subscribes.

## Common envelope

```json theme={null}
{
  "id": "0c92ee55-2b2a-44ec-bdcc-91c73d48367c",
  "type": "document.processed",
  "sequence": 4821,
  "created_at": "2026-08-11T15:20:31.123Z",
  "agent_id": "ae8bc83f-d248-484d-909f-49b32e5eac0a",
  "data": {
    "document": {}
  }
}
```

| Field        | Type            | Meaning                                                                 |
| ------------ | --------------- | ----------------------------------------------------------------------- |
| `id`         | UUID            | Stable event identity. Use it to deduplicate.                           |
| `type`       | string          | Event type from the supported catalog, or a future additive event type. |
| `sequence`   | integer         | Global increasing sequence used to reject stale snapshots.              |
| `created_at` | ISO 8601 string | Time Fluent recorded the event.                                         |
| `agent_id`   | UUID            | Agent that owns the event.                                              |
| `data`       | object          | Event-family data described below.                                      |

`sequence` is globally assigned, so values visible to one team contain gaps. Concurrent transactions can also become visible in a different order from their allocated sequence. Do not use sequence gaps to infer lost events. Use it only to avoid applying an older snapshot over a newer one for the same resource.

## Document payload

All `document.*` and `approval.*` events include `data.document`:

```json theme={null}
{
  "document": {
    "id": "7d20b40d-51be-481d-8f39-808b62084ef7",
    "type": "ap_invoice",
    "status": "processed",
    "previous_status": "approved",
    "run_id": "45e98f7a-31b8-4f26-b760-d8d3ec82df15",
    "conversation_id": "conversation_abc",
    "created_at": "2026-08-11T15:19:02.000Z",
    "processed_at": "2026-08-11T15:20:31.000Z",
    "error": null,
    "url": "https://www.fluenterp.com/w/acme/documents/7d20b40d-..."
  }
}
```

| Field             | Meaning                                                            |
| ----------------- | ------------------------------------------------------------------ |
| `id`              | Durable Fluent document ID.                                        |
| `type`            | Document type reported by the structured data, or `unknown`.       |
| `status`          | Document state at event creation time.                             |
| `previous_status` | Previous state for status transitions; otherwise `null` or absent. |
| `run_id`          | Public API run that produced the document, or `null`.              |
| `conversation_id` | Source conversation, or `null`.                                    |
| `created_at`      | Document creation time.                                            |
| `processed_at`    | Successful system-of-record processing time, or `null`.            |
| `error`           | Current document error, or `null`.                                 |
| `url`             | Fluent product URL for authorized users, or `null` for test data.  |

The payload does not contain `document_data`. Fetch the current document with:

```bash theme={null}
curl "https://www.fluenterp.com/api/v1/documents/$DOCUMENT_ID" \
  -H "Authorization: Bearer $FLUENT_API_KEY"
```

## Document update payload

`document.updated` adds `data.update`:

```json theme={null}
{
  "document": {},
  "update": {
    "version_number": 3,
    "trigger": "user_edit",
    "updated_by": {
      "id": "d38a967b-912d-4ed7-aad0-acde856662ab",
      "name": "Maria Perez"
    }
  }
}
```

`trigger` is `user_edit` or `rollback`. `updated_by` can be `null`, and its `name` can also be `null`.

## Approval payload

`approval.*` events add `data.approval`:

```json theme={null}
{
  "document": {},
  "approval": {
    "stage_name": "Financial review",
    "stage_position": 1,
    "stage_count": 2,
    "approver": {
      "id": "d38a967b-912d-4ed7-aad0-acde856662ab",
      "name": "Maria Perez",
      "email": "maria@example.com"
    },
    "status": "approved",
    "note": "Amounts verified",
    "reason": null
  }
}
```

| Field            | Meaning                                                        |
| ---------------- | -------------------------------------------------------------- |
| `stage_name`     | Configured stage label, or `null`.                             |
| `stage_position` | Position of this stage in the flow.                            |
| `stage_count`    | Number of distinct stages currently recorded for the document. |
| `approver`       | Assigned user, or `null` for an unassigned skipped unit.       |
| `status`         | `pending`, `approved`, `rejected`, or `skipped`.               |
| `note`           | Human approval/rejection note, or `null`.                      |
| `reason`         | Skip explanation when available; otherwise `null` or absent.   |

An `approval.skipped` payload can contain `approver: null`, `note: null`, and a non-null `reason`.

## Run payload

`run.*` events contain `data.run` instead of a document:

```json theme={null}
{
  "run": {
    "id": "45e98f7a-31b8-4f26-b760-d8d3ec82df15",
    "status": "completed",
    "error": null,
    "document_ids": [
      "7d20b40d-51be-481d-8f39-808b62084ef7"
    ]
  }
}
```

`document_ids` contains every document associated with the API run and can be empty. On `run.failed`, inspect `error`; documents produced before the failure still appear in the list.

## Correlate runs and documents

Store `run_id` from `POST /api/v1/agents/{agent_id}/run` with your source record. Every document created by that API run carries the same ID in `data.document.run_id`, and the terminal run event lists its document IDs.

Do not correlate on extracted values such as supplier, invoice number, or purchase-order number. Those values can change during extraction or review and are not guaranteed unique.

## Dispatch events safely

Treat the event name as an open string rather than a closed enum. This keeps your receiver compatible when Fluent adds an event type.

```ts theme={null}
const event = JSON.parse(rawBody) as {
  id: string;
  type: string;
  sequence: number;
  agent_id: string;
  data: Record<string, unknown>;
};

switch (event.type) {
  case "document.processed":
    // Fetch and reconcile the document.
    break;
  default:
    // Record unknown event types and acknowledge them.
    break;
}
```
