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

# List Recordings & Uploads Endpoint

> List every call recording and uploaded file in your workspace, newest first, with signed playback links.

<Info>This endpoint is in **beta**. The interface is stable enough to build on; we may add fields as it matures.</Info>

## Overview

Returns two separate lists for your workspace:

* **`recordings`** — audio from calls placed on your Tuco line, captured automatically.
* **`items`** — files *you* uploaded, via [Upload attachment](/api-reference/upload-attachment) or the **Leads → Upload audio etc** tab.

Use this when you want everything in the workspace. To pull the calls made to one
specific lead, use [Get call recordings](/api-reference/endpoint/get-recordings)
instead — that endpoint takes a phone number and returns full call timing.

***

## Making a request

* **Method**: `GET`
* **URL**: `https://app.tuco.ai/api/attachments/list`
* **Headers**: `Authorization: Bearer tuco_...`

```bash theme={null}
curl "https://app.tuco.ai/api/attachments/list?limit=20" \
  -H "Authorization: Bearer tuco_your_key"
```

### Query parameters

<ParamField query="limit" type="number">
  Max entries to return per list. Default `20`, min `1`, max `100`.
</ParamField>

<ParamField query="cursor" type="string">
  Opaque cursor from a previous response's `nextCursor`, to page through **uploads**.
</ParamField>

<ParamField query="recordingsCursor" type="string">
  Opaque cursor from a previous response's `recordingsNextCursor`, to page through
  **recordings**. Recordings and uploads paginate independently — see
  [Paging](#paging).
</ParamField>

***

## Response

```json theme={null}
{
  "recordings": [
    {
      "url": "https://app.tuco.ai/api/attachments/crm-media/6aac0729....mp3?u=...&exp=...&sig=...",
      "name": "Call → +15145550123 · 3:40",
      "size": 878733,
      "uploadedAt": "2026-09-17T15:28:41.632Z",
      "pathname": "workspaces/org_.../recordings/6aac0729....mp3",
      "isAudio": true,
      "kind": "recording",
      "callId": "6aac072927e54a4003b72623",
      "toE164": "+15145550123",
      "fromE164": "+13025550999",
      "durationSec": 220
    }
  ],
  "recordingsNextCursor": null,
  "recordingsHasMore": false,
  "items": [
    {
      "url": "https://app.tuco.ai/api/attachments/crm-media/vn.m4a?u=...&exp=...&sig=...",
      "name": "vn.m4a",
      "size": 45056,
      "uploadedAt": "2026-09-18T10:02:11.000Z",
      "pathname": "workspaces/org_.../attachments/....m4a",
      "isAudio": true,
      "kind": "upload"
    }
  ],
  "nextCursor": null,
  "hasMore": false
}
```

<ResponseField name="recordings" type="array">
  Call recordings, newest first.

  <Expandable title="recording fields">
    <ResponseField name="url" type="string">Signed playback/download URL. See [Links expire](#links-expire).</ResponseField>
    <ResponseField name="name" type="string">Display label, e.g. `Call → +15145550123 · 3:40`.</ResponseField>
    <ResponseField name="size" type="number">File size in bytes.</ResponseField>
    <ResponseField name="uploadedAt" type="string">When the call was answered; falls back to when it was requested, then to when the audio was stored.</ResponseField>
    <ResponseField name="pathname" type="string">Internal storage path. Informational only.</ResponseField>
    <ResponseField name="isAudio" type="boolean">Always `true` for recordings.</ResponseField>
    <ResponseField name="kind" type="string">Always `recording`. Use this to tell the two lists apart if you merge them.</ResponseField>
    <ResponseField name="callId" type="string">The call's ID — pass it to your own systems, or match it against [Get call recordings](/api-reference/endpoint/get-recordings).</ResponseField>
    <ResponseField name="toE164" type="string">Number that was called. `null` if the call record is unavailable.</ResponseField>
    <ResponseField name="fromE164" type="string">Caller ID the lead saw. `null` if the call record is unavailable.</ResponseField>
    <ResponseField name="durationSec" type="number">Talk time in seconds. `null` if the call record is unavailable.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="recordingsNextCursor" type="string">Cursor for the next page of recordings, or `null`.</ResponseField>
<ResponseField name="recordingsHasMore" type="boolean">Whether more recordings exist beyond this page.</ResponseField>

<ResponseField name="items" type="array">
  Files you uploaded, newest first.

  <Expandable title="upload fields">
    <ResponseField name="url" type="string">Signed download URL.</ResponseField>
    <ResponseField name="name" type="string">Original filename.</ResponseField>
    <ResponseField name="size" type="number">File size in bytes.</ResponseField>
    <ResponseField name="uploadedAt" type="string">When it was uploaded.</ResponseField>
    <ResponseField name="pathname" type="string">Internal storage path. Informational only.</ResponseField>
    <ResponseField name="isAudio" type="boolean">Whether the file is a supported audio type.</ResponseField>
    <ResponseField name="kind" type="string">Always `upload`.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="nextCursor" type="string">Cursor for the next page of uploads, or `null`.</ResponseField>
<ResponseField name="hasMore" type="boolean">Whether more uploads exist beyond this page.</ResponseField>

***

## Paging

Recordings and uploads are **paged independently**, each with its own cursor. Pass
`recordingsCursor` to advance recordings and `cursor` to advance uploads; you can
send both, either, or neither.

```bash theme={null}
# next page of recordings only
curl "https://app.tuco.ai/api/attachments/list?limit=20&recordingsCursor=CURSOR_FROM_LAST_RESPONSE" \
  -H "Authorization: Bearer tuco_your_key"
```

<Note>
  `limit` applies to **each** list, so `limit=20` can return up to 20 recordings *and*
  up to 20 uploads in one response.
</Note>

## Links expire

<Warning>
  Every `url` is a **signed link that expires**. Do not store it — keep the `callId`
  (for recordings) or `pathname`, and call this endpoint again when you need fresh
  links. The audio itself is kept indefinitely; only the link is time-limited.
</Warning>

Links from this endpoint are valid for **1 year**. Note that the same recording
requested via [Get call recordings](/api-reference/endpoint/get-recordings) returns
a link valid for **30 days** — treat both as short-lived and re-fetch rather than
caching either.

## Format

Call recordings are **MP3, mono** — both sides of the conversation are mixed into a
single channel. Good for playback and transcription; not suited to separating the
speakers.

## Scope

You only ever see your own workspace's recordings and uploads. The API key you
authenticate with determines the workspace, and there is no parameter to query
another one.

***

## Errors

| HTTP | Meaning                                                                                                        |
| ---- | -------------------------------------------------------------------------------------------------------------- |
| 401  | Missing or invalid API key                                                                                     |
| 500  | Unexpected server error — the response body is `{ "error": "Could not load your uploads. Please try again." }` |
