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

# Uploading work items

> Push back-office work records and their events to Rulebase one at a time or by CSV, and match each one to the right agent.

Upload back-office work item records and their event logs: compliance reviews, fraud investigations, payment disputes, and KYC reviews.

All work item requests use `https://api2.rulebase.co` (or `https://eu.api2.rulebase.co` on EU) and require a Rulebase API key in the `Authorization: Bearer <key>` header.

The canonical paths are versioned: `/v1/work_items`. The older unversioned paths (`/work_items`) still work, but they answer with `Deprecation` and `Sunset` headers and will be removed. Move to `/v1` when you next touch the integration.

If you are scripting this from an agent or a CI job, the [Rulebase CLI](/guides/cli) wraps every endpoint below with JSON in and JSON out.

## Rate limits

Work item endpoints allow about 5,000 requests per minute per organization. Over that, the API responds with `429 Too Many Requests` and a `Retry-After` header giving the seconds to wait. For large volumes, use bulk CSV upload, which carries many rows in one request.

## When to use work items vs conversations

| If the work is...                                                                   | Use              |
| ----------------------------------------------------------------------------------- | ---------------- |
| A customer interaction (call, chat, email)                                          | `/conversations` |
| Back-office work an agent did in your internal admin tool (no customer on the line) | `/v1/work_items` |

Work items preserve the source work record, the responsible employee, customer-specific attributes, and the source-system events. Uploading a work item stores the record and its events; it does not score or evaluate it.

## Upload methods

Pick based on how your source system exports data.

1. **Single work item.** `POST /v1/work_items` with a JSON body. For systems that push individual items in real time. It is create-only: a second push of the same `external_id` returns `409`.
2. **JSON batch apply.** `POST /v1/work_items/batch` with up to 100 work items in one request. Each item is created, updated, or restored by `external_id`, so it is the endpoint to use when you re-send records you may have sent before. Preferred for agents and scripted backfills.
3. **Bulk CSV upload.** `POST /v1/work_items/upload` with a multipart CSV of many items. For periodic exports out of a system that speaks CSV.

Use apply rather than CSV upload when you already hold structured records: it is synchronous, tells you per item whether it created, updated, restored, or failed, and supports `dry_run`. CSV upload is asynchronous and reports per-file counts you poll for.

***

## Method 1: single work item

`POST /v1/work_items` with the work item details as a JSON body:

```bash theme={null}
curl -X POST https://api2.rulebase.co/v1/work_items \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "work_item": {
      "external_id": "TASK-9182",
      "agent_email": "alex@example.com",
      "agent_name": "Alex Smith",
      "agent_external_id": "EMP-447",
      "type": "fraud_review",
      "status": "completed",
      "completed_at": "2026-05-12T14:32:00Z",
      "custom_attributes": {
        "risk_score": 78
      },
      "events": [
        {
          "external_id": "EVENT-551",
          "actor_external_id": "EMP-447",
          "content": "Reviewed the account activity and approved the refund.",
          "occurred_at": "2026-05-12T14:31:00Z"
        }
      ]
    }
  }'
```

A successful response returns the created work item:

```json theme={null}
{
  "data": {
    "id": "0197ac58-70c9-7bc0-9ba8-1bb7ce8f78cc",
    "external_id": "TASK-9182",
    "agent": {
      "id": "employee_3kV9m2qL7xN4pR8tW1yB6zAa",
      "name": "Alex Smith",
      "email": "alex@example.com",
      "external_id": "EMP-447",
      "avatar_url": "https://cdn.example.com/agents/alex.png"
    },
    "type": "fraud_review",
    "status": "completed",
    "qa_score": null,
    "completed_at": "2026-05-12T14:32:00Z",
    "custom_attributes": {
      "risk_score": 78
    },
    "events": [
      {
        "id": "0197ac58-70c9-7bc0-9ba8-1bb7ce8f78dd",
        "external_id": "EVENT-551",
        "actor": {
          "id": "employee_3kV9m2qL7xN4pR8tW1yB6zAa",
          "name": "Alex Smith",
          "email": "alex@example.com",
          "external_id": "EMP-447",
          "avatar_url": "https://cdn.example.com/agents/alex.png"
        },
        "content": "Reviewed the account activity and approved the refund.",
        "occurred_at": "2026-05-12T14:31:00Z",
        "created_at": "2026-05-12T14:35:00Z"
      }
    ],
    "created_at": "2026-05-12T14:35:00Z",
    "updated_at": "2026-05-12T14:35:00Z"
  }
}
```

The response `id` is a **work item** UUID. Fetch it later with [`GET /v1/work_items/{id}`](/api-v2-reference/get-work-item). That path accepts a work item id only; a bulk upload job id returns `404`.

Full REST spec: [Create a work item](/api-v2-reference/create-work-item).

***

## Method 2: bulk CSV upload

Upload a CSV to `POST /v1/work_items/upload` as `multipart/form-data`:

```bash theme={null}
curl -X POST https://api2.rulebase.co/v1/work_items/upload \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "upload[file]=@work-items-2026-05-12.csv"
```

A successful response returns an **upload job** UUID and a `pending` status:

```json theme={null}
{
  "data": {
    "id": "0197ac5b-a956-79b6-8ad0-9c735ee58a49",
    "status": "pending",
    "total_rows": 0,
    "succeeded_rows": 0,
    "failed_rows": 0,
    "error": null,
    "created_at": "2026-05-12T14:35:00Z",
    "updated_at": "2026-05-12T14:35:00Z"
  }
}
```

That `id` is an upload job id. Poll it with [`GET /v1/work_items/upload/{id}`](/api-v2-reference/get-work-item-upload):

```bash theme={null}
curl -i "https://api2.rulebase.co/v1/work_items/upload/0197ac5b-a956-79b6-8ad0-9c735ee58a49" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Do not poll `GET /v1/work_items/{id}` with an upload job id. That endpoint looks up a single work item and returns `404` for an upload job.

Poll until the upload reaches a terminal status:

* `completed`: every row succeeded.
* `completed_with_errors`: at least one row failed, while valid rows were still imported.
* `failed`: the file could not be processed, such as when required headers are missing or storage is unavailable.

Example terminal response:

```json theme={null}
{
  "data": {
    "id": "0197ac5b-a956-79b6-8ad0-9c735ee58a49",
    "status": "completed_with_errors",
    "total_rows": 968,
    "succeeded_rows": 885,
    "failed_rows": 83,
    "error": null,
    "created_at": "2026-05-12T14:35:00Z",
    "updated_at": "2026-05-12T14:35:11Z"
  }
}
```

Invalid rows do not block valid rows. Retrying an upload job skips rows that already succeeded, and duplicate `external_id` values resolve to the same work item.

### Bulk upload requirements

* **Content-Type:** `multipart/form-data`
* **File format:** CSV with a header row
* **Header row required;** column order is flexible
* **Maximum file size:** 25 MB

Full REST spec: [Upload work items in bulk](/api-v2-reference/upload-work-items).

***

## Work item schema reference

| Field               | Required                                             | Type   | Description                                                                                                                                                |
| ------------------- | ---------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `external_id`       | Yes                                                  | string | Unique identifier for this work item in your system. Used to deduplicate re-uploads.                                                                       |
| `agent_email`       | No (recommended)                                     | string | Work email of the agent. Prefer providing email or external ID when available.                                                                             |
| `agent_external_id` | No (recommended)                                     | string | Stable employee identifier from your system.                                                                                                               |
| `agent_name`        | No                                                   | string | Display name stored on the employee record.                                                                                                                |
| `type`              | No                                                   | string | Customer-defined work item type. New work items default to `unknown` when omitted.                                                                         |
| `status`            | No                                                   | string | Work item status. One of `pending`, `in_progress`, `completed`, or `cancelled`. New work items default to `pending` when omitted.                          |
| `completed_at`      | Required when `status` is `completed` or `cancelled` | string | ISO 8601 timestamp for when the work item reached a terminal state. Sending `pending` or `in_progress` without a timestamp clears a prior completion date. |
| `custom_attributes` | No                                                   | object | Free-form key/value pairs for anything else, such as outcome or risk category.                                                                             |
| `events`            | No                                                   | array  | Source-system events associated with the work item.                                                                                                        |

Customer-specific outcomes and metadata go in `custom_attributes`. The keys `type`, `status`, and `priority` are reserved and not stored inside `custom_attributes`; send `type` and `status` as top-level fields.

Incomplete imports (for example notes-only exports) may omit agent, type, and status. Rulebase stores stubs so events can attach; UI shows **Unknown agent** until you backfill. AI QA scorecards will not apply until an agent is present.

### How agents are matched

On receiving a work item, Rulebase looks up the agent against your existing employee records:

1. **Match by `agent_external_id`** if provided.
2. Otherwise, **match by `agent_email`**.
3. If neither matches and an identifier was provided, a new employee is created from the fields you sent.
4. If neither agent field is provided, the work item is stored without an agent.

Batch apply and CSV upload resolve an existing work item by `external_id` within your organization (`PATCH /v1/work_items/{id}` instead targets the Rulebase work item ID, and cannot change `external_id`). Re-sending the same `external_id` refreshes provided fields from the new payload; omitted/null agent, type, and status preserve existing values so a thin re-upload does not erase a prior backfill. Custom attributes are merged: the new upload wins on shared keys, and keys only on the stored record are kept. There is no separate employee roster API. You enrich employee records through the agent objects you carry on the work items you push.

Name alone will not match or create an agent. Provide `agent_email` or `agent_external_id`; `agent_name` only enriches the matched employee.

***

## CSV column reference

CSV uploads use flat columns that map onto the JSON schema.

| Column              | Required                                           | Maps to                   |
| ------------------- | -------------------------------------------------- | ------------------------- |
| `ID`                | Yes                                                | `external_id`             |
| `Agent email`       | No (recommended)                                   | `agent_email`             |
| `Agent external ID` | No (recommended)                                   | `agent_external_id`       |
| `Agent name`        | No                                                 | `agent_name`              |
| `Completed at`      | Required when Status is `completed` or `cancelled` | `completed_at`            |
| `Type`              | No                                                 | `type`                    |
| `Status`            | No                                                 | `status`                  |
| `custom_<key>`      | No                                                 | `custom_attributes.<key>` |

`Status` must be one of `pending`, `in_progress`, `completed`, or `cancelled`, and is normalized to lowercase. On a new work item, blank Status stores `pending` and blank Type stores `unknown`; on a re-upload, blank Status and Type preserve their stored values. `Completed at` is required when Status is `completed` or `cancelled`. A blank completion date with `pending` or `in_progress` clears a prior completion date, while omitting both the Status and Completed at columns preserves it. Legacy snake\_case headers such as `external_id`, `agent_email`, and `completed_at` are also accepted. Any column prefixed with `custom_` is flattened into `custom_attributes`. For example, `custom_risk_score` becomes `custom_attributes.risk_score`. The reserved columns `custom_type`, `custom_status`, and `custom_priority` are ignored.

### Example CSV

```csv theme={null}
ID,Agent email,Agent name,Completed at,Type,Status
TASK-9182,alex@example.com,Alex Smith,2026-05-12T14:32:00Z,fraud_review,completed
TASK-9183,sam@example.com,Sam Patel,,kyc_verification,pending
TASK-9184,alex@example.com,Alex Smith,2026-05-12T15:02:00Z,account_closure,cancelled
```

***

***

## Method 2: JSON batch apply

`POST /v1/work_items/batch` takes 1-100 work items in a `work_items` array and applies them in order:

```bash theme={null}
curl -X POST https://api2.rulebase.co/v1/work_items/batch \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "work_items": [
      { "external_id": "TASK-9182", "agent_email": "alex@example.com", "type": "fraud_review" },
      { "external_id": "TASK-9183", "type": "kyc_verification", "custom_attributes": { "risk_score": 12 } }
    ]
  }'
```

The response keeps your input order and reports each item separately:

```json theme={null}
{
  "data": {
    "dry_run": false,
    "results": [
      {
        "index": 0,
        "external_id": "TASK-9182",
        "status": "updated",
        "data": { "id": "0197ac58-70c9-7bc0-9ba8-1bb7ce8f78cc" }
      },
      {
        "index": 1,
        "external_id": "TASK-9183",
        "status": "created",
        "data": { "id": "0197ac58-8f10-7a41-bd21-4a6d54c1e900" }
      }
    ],
    "summary": {
      "total": 2,
      "created": 1,
      "updated": 1,
      "unchanged": 0,
      "restored": 0,
      "error": 0
    }
  }
}
```

* Statuses are `created`, `updated`, `unchanged`, `restored`, or `error`.
* A failed item does not roll back the items that succeeded; check `summary.error`.
* Work items you leave out of the request are never deleted.
* Sending an `external_id` that was deleted restores it and reports `restored`.
* Set `"dry_run": true` to get the same result shape without writing anything.

Full REST spec: [Apply a batch of work items](/api-v2-reference/apply-work-items).

***

## Updating and deleting

`PATCH /v1/work_items/{id}` updates a stored work item. Omitted fields keep their values, `custom_attributes` merge with the stored ones, and `external_id` cannot be changed.

```bash theme={null}
curl -X PATCH https://api2.rulebase.co/v1/work_items/0197ac58-70c9-7bc0-9ba8-1bb7ce8f78cc \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "work_item": { "status": "completed", "completed_at": "2026-05-12T14:32:00Z" } }'
```

`DELETE /v1/work_items/{id}` soft-deletes a work item. It disappears from reads and lists and stops being summarized, while existing evaluations that reference it keep resolving. The call is idempotent: deleting an already-deleted work item still returns `200` with `"deleted": false`. There is no hard delete and no bulk delete. To bring a work item back, apply its `external_id` again.

***

## Listing work items

`GET /v1/work_items` returns work items newest-completion-first with cursor pagination:

```bash theme={null}
curl "https://api2.rulebase.co/v1/work_items?limit=50&status=completed" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Filters: `external_id` (exact match), `status` (repeatable), `completed_after` and `completed_before` (ISO 8601), and `query` (substring match on external ID and type). Pass the returned `meta.page.next` value back as `cursor` to fetch the next page; it is `null` on the last page. Deleted work items are excluded.

***

## Retrieving a work item

Fetch a work item by its Rulebase work item id (from `POST /v1/work_items` or a successful import row) with [`GET /v1/work_items/{id}`](/api-v2-reference/get-work-item). For bulk upload progress, use [`GET /v1/work_items/upload/{id}`](/api-v2-reference/get-work-item-upload) with the upload job id.

```json theme={null}
{
  "data": {
    "id": "0197ac58-70c9-7bc0-9ba8-1bb7ce8f78cc",
    "external_id": "TASK-9182",
    "agent": {
      "id": "employee_3kV9m2qL7xN4pR8tW1yB6zAa",
      "name": "Alex Smith",
      "email": "alex@example.com",
      "external_id": "EMP-447",
      "avatar_url": "https://cdn.example.com/agents/alex.png"
    },
    "type": "fraud_review",
    "status": "completed",
    "qa_score": null,
    "completed_at": "2026-05-12T14:32:00Z",
    "custom_attributes": {
      "risk_score": 78
    },
    "events": [],
    "created_at": "2026-05-12T14:35:00Z",
    "updated_at": "2026-05-12T14:51:00Z"
  }
}
```

`qa_score` is `null` until the work item is evaluated; after a QA evaluation is saved, it holds the current score from `0` to `1`.

## Adding work item events

Send events inline when creating a work item, or add them later with `POST /v1/work_items/{id}/events`. Rulebase upserts events by `external_id` within your organization.

```bash theme={null}
curl -X POST https://api2.rulebase.co/v1/work_items/0197ac58-70c9-7bc0-9ba8-1bb7ce8f78cc/events \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": {
      "external_id": "EVENT-552",
      "actor_email": "alex@example.com",
      "content": "Submitted the review.",
      "occurred_at": "2026-05-12T14:32:00Z"
    }
  }'
```

Only `external_id` is required. Actor identity, raw `content`, and `occurred_at` are optional. Actors use the same email/external-ID matching behavior as work item agents. `GET /v1/work_items/{id}` returns events in chronological order.
