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

# Activities, Files and History

> Tasks, attachments and the event log of each deal

## Activities (todos)

An activity is a task linked to a deal: a call, a meeting, a follow-up.

### Valid types

| `type`      | Meaning   | Use                                          |
| ----------- | --------- | -------------------------------------------- |
| `Ligação`   | Call      | Contact by phone                             |
| `Reunião`   | Meeting   | Scheduled meeting                            |
| `Follow-up` | Follow-up | Getting back in touch                        |
| `Tarefa`    | Task      | Generic — the default when `type` is omitted |

<Warning>
  The `type` values are **literal Portuguese strings sent to and returned by the API**, accents
  included. Send `Ligação`, `Reunião`, `Follow-up` or `Tarefa` exactly as written — the English
  column above is only a gloss for readers, never a value to send. Any `type` outside this list
  returns **400**.
</Warning>

### The Activity object

<ResponseField name="id" type="string">
  Activity UUID
</ResponseField>

<ResponseField name="deal_id" type="string">
  Deal UUID
</ResponseField>

<ResponseField name="type" type="string">
  Activity type
</ResponseField>

<ResponseField name="text" type="string">
  Activity description
</ResponseField>

<ResponseField name="due_at" type="string">
  Due date (ISO 8601), or absent
</ResponseField>

<ResponseField name="booking_id" type="string">
  UUID of the booking in the Bookings module, when the activity came from a scheduled meeting
</ResponseField>

<ResponseField name="done" type="boolean">
  Whether the activity is completed
</ResponseField>

<ResponseField name="overdue" type="boolean">
  Calculated at read time: `true` when the activity is pending and `due_at` has already passed
</ResponseField>

### List activities

<Card>
  <strong>GET</strong> `/backend/pipeline/deals/{id}/todos`
</Card>

Requires `pipeline.view`.

### Create an activity

<Card>
  <strong>POST</strong> `/backend/pipeline/deals/{id}/todos`
</Card>

Requires `pipeline.deals.edit`.

<ParamField body="text" type="string">
  Activity description. When omitted or empty, the `type` itself is used as the description.
</ParamField>

<ParamField body="type" type="string" default="Tarefa">
  One of the valid types above
</ParamField>

<ParamField body="due_at" type="string">
  Due date (ISO 8601)
</ParamField>

<ParamField body="booking_id" type="string">
  UUID of a booking already created in Bookings, to link the activity to the real meeting
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.leavo.ai/backend/pipeline/deals/dd0e8400-e29b-41d4-a716-446655440000/todos" \
    -H "Authorization: Bearer your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "Ligação",
      "text": "Call back about the proposal",
      "due_at": "2026-07-25T14:00:00Z"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Created theme={null}
  {
    "id": "110e8400-e29b-41d4-a716-446655440000",
    "deal_id": "dd0e8400-e29b-41d4-a716-446655440000",
    "type": "Ligação",
    "text": "Call back about the proposal",
    "due_at": "2026-07-25T14:00:00Z",
    "done": false,
    "overdue": false,
    "created_at": "2026-07-21T10:00:00Z"
  }
  ```
</ResponseExample>

### Update an activity

<Card>
  <strong>PUT</strong> `/backend/pipeline/todos/{id}`
</Card>

Requires `pipeline.deals.edit`. Omitted fields stay unchanged.

<ParamField body="text" type="string">
  New description. If sent, it cannot be empty — in that case the response is **400**.
</ParamField>

<ParamField body="done" type="boolean">
  Marks or unmarks it as completed
</ParamField>

<ParamField body="due_at" type="string">
  New due date (ISO 8601)
</ParamField>

<Note>
  An activity's `type` cannot be changed after it is created.
</Note>

### Delete an activity

<Card>
  <strong>DELETE</strong> `/backend/pipeline/todos/{id}`
</Card>

Requires `pipeline.deals.edit`. Returns `{ "success": true }`.

***

## Files

Attachments follow the **two-phase** pattern already used across the rest of the platform: first
the file is sent to [File Upload](/en/api-reference/uploads/upload), then the resulting
`upload_id` is linked to the deal.

<Steps>
  <Step title="Send the file">
    `POST /backend/uploads` with the file. The upload is born with status `PENDING`.
  </Step>

  <Step title="Link it to the deal">
    `POST /backend/pipeline/deals/{id}/files` with the `upload_id`. The upload becomes `ASSOCIATED`.
  </Step>
</Steps>

### List files

<Card>
  <strong>GET</strong> `/backend/pipeline/deals/{id}/files`
</Card>

Requires `pipeline.view`.

<ResponseExample>
  ```json 200 OK theme={null}
  [
    {
      "id": "220e8400-e29b-41d4-a716-446655440000",
      "upload_id": "330e8400-e29b-41d4-a716-446655440000",
      "filename": "sales-proposal.pdf",
      "file_url": "https://storage.leavo.ai/...",
      "mime_type": "application/pdf",
      "size": 148392,
      "created_at": "2026-07-21T10:05:00Z"
    }
  ]
  ```
</ResponseExample>

### Attach a file

<Card>
  <strong>POST</strong> `/backend/pipeline/deals/{id}/files`
</Card>

Requires `pipeline.deals.edit`.

<ParamField body="upload_id" type="string" required>
  UUID of the upload returned by the previous step
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.leavo.ai/backend/pipeline/deals/dd0e8400-e29b-41d4-a716-446655440000/files" \
    -H "Authorization: Bearer your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{ "upload_id": "330e8400-e29b-41d4-a716-446655440000" }'
  ```
</RequestExample>

Returns **201 Created** with the file.

### Remove a file

<Card>
  <strong>DELETE</strong> `/backend/pipeline/deal-files/{id}`
</Card>

Requires `pipeline.deals.edit`. The `{id}` is the **attachment id** (`DealFile`), not the `upload_id`.

***

## History (events)

Each deal keeps a persisted event log — it is not derived at read time. This is what feeds the
deal's history tab.

<Card>
  <strong>GET</strong> `/backend/pipeline/deals/{id}/events`
</Card>

Requires `pipeline.view`. Paginated response.

### Query Parameters

<ParamField query="page" type="integer" default="1">
  Desired page
</ParamField>

<ParamField query="limit" type="integer" default="50">
  Items per page. Values outside 1–200 fall back to the default of 50.
</ParamField>

### Event types

| `event_type`       | When it happens         |
| ------------------ | ----------------------- |
| `deal_created`     | Deal created            |
| `stage_moved`      | Moved to another stage  |
| `deal_won`         | Moved to the won stage  |
| `deal_lost`        | Moved to the lost stage |
| `note_added`       | Note changed            |
| `todo_created`     | Activity created        |
| `todo_completed`   | Activity completed      |
| `owner_changed`    | Owner changed           |
| `tag_added`        | Tag added               |
| `tag_removed`      | Tag removed             |
| `file_attached`    | File attached           |
| `file_removed`     | File removed            |
| `meeting_booked`   | Meeting scheduled       |
| `products_updated` | Deal products changed   |

### Event author (`actor_type`)

| Value    | Meaning                                                              |
| -------- | -------------------------------------------------------------------- |
| `user`   | An authenticated person                                              |
| `ai`     | The AI assistant                                                     |
| `system` | Automatic action — for example, a deal created by an inbound webhook |

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "data": [
      {
        "id": "440e8400-e29b-41d4-a716-446655440000",
        "event_type": "deal_lost",
        "title": "Negócio marcado como perdido",
        "description": "Motivo: Fechou com concorrente",
        "actor_type": "user",
        "actor_name": "Ana Ribeiro",
        "metadata": {
          "from_stage_id": "cc0e8400-e29b-41d4-a716-446655440000",
          "to_stage_id": "cc3e8400-e29b-41d4-a716-446655440000"
        },
        "created_at": "2026-07-21T09:40:00Z"
      }
    ],
    "pagination": {
      "current_page": 1,
      "total_pages": 1,
      "total_count": 8,
      "has_next": false,
      "has_prev": false
    }
  }
  ```
</ResponseExample>

<Note>
  The `title` and `description` of an event are generated by the server in Portuguese and stored
  as-is. Use `event_type` and `metadata` if you need to render your own English labels.
</Note>
