# Files and tasks

> How a file gets into the Chatfuel API — REST upload, then a FileID a mutation can reference — the File lifecycle, and how a long-running job reports its progress.

Page: https://sdk.chatfuel.com/docs/reference/chatfuel-api/files-and-tasks
Markdown: https://sdk.chatfuel.com/docs/reference/chatfuel-api/files-and-tasks.md

GraphQL never accepts file bytes, so every file starts as a REST upload that hands back a `FileID`
a mutation can then reference.

```graphql
type File {
  id: FileID!
  url: String!
  type: FileType!
  """
  if the status is Expired - do not request any other fields of this file. The file does not exist
  """
  status: FileStatus!
  """in bytes"""
  size: Int
}
```

## Uploading [#uploading]

Every endpoint is a `POST` with a multipart `file` field and the same `Authorization: Bearer <token>` header the GraphQL endpoint takes. `fileType` is one of `Image`, `Video`, `Audio`,
`Document`.

| Endpoint                                                             | What it is for                                                                                            |
| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `{base}/api/filestorage/upload/livechat?fileType=&botID=&contactID=` | Chat attachments.                                                                                         |
| `{base}/api/filestorage/upload/bot?fileType=&botID=`                 | CSV imports, catalog images, specialist avatars.                                                          |
| `{base}/api/filestorage/upload/plugin?...`                           | WhatsApp template header media, and flow-builder block media, where `pluginID` is the block element's id. |
| `{base}/api/filestorage/upload/widget?...`                           | The web widget's avatar.                                                                                  |
| `{base}/api/filestorage/upload/useraccount?...`                      | User profile pictures.                                                                                    |

Upload failures mirror the GraphQL codes: a non-2xx response carries `FileTooBig` or
`FileContentTypeNotSupported` in its body. The success shape is less settled — the SDK's helper
accepts `id`, `fileID` or `fileId`, either at the top level or nested one level under `file`,
`result` or `data`, because the endpoint's response key is not pinned by any documentation.

## `Expired` means the file is gone [#expired-means-the-file-is-gone]

```graphql
enum FileStatus {
  """the file was either deleted because it expired, or never existed."""
  Expired
  NotDownloaded
  DownloadInProgress
  Downloaded
  Failed
}
```

On `Expired`, stop: do not request and do not render any other field of that file. For a remote
file whose bytes were never fetched, `fileStartDownload(id:)` asks the platform to fetch them, and
`file(id:)` polls until the status reads `Downloaded`.

## Tasks [#tasks]

A long-running job — a CSV contact export, a specialist's Google Calendar sync — returns a `Task`.

```graphql
type Task {
  id: TaskID!
  """
  holds the status history of the process.
    If you need the current status - take the most recent one by date
  """
  statuses: [TaskStatus!]!
  """number of already completed points out of the total (totalPoints)"""
  completedPoints: Int!
  """
  the maximum number of points that have to be completed within this process.
    It is set when the process is created and never changes afterwards.
    The ratio of completedPoints to totalPoints shows the current progress of the process.
  """
  totalPoints: Int!
  data: TaskData!
  """
  after this date the process should be treated as failed by timeout (even if there is no explicit failed status)
  """
  deadline: Time!
}
```

| Field                             | How to read it                                                                                                                                                                                                                |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `statuses`                        | A history, not a value. The current status is the entry with the latest `startedAt` — not `statuses[0]`. The types are `Created`, `InProgress`, `Paused`, `Cancelled`, `Failed`, `Finished`.                                  |
| `completedPoints` / `totalPoints` | Approximate. `completedPoints` can exceed `totalPoints`, and a task can finish before the two converge, so render progress defensively.                                                                                       |
| `deadline`                        | A wall-clock cutoff, not a hint.                                                                                                                                                                                              |
| `data`                            | A federated interface. Select `__typename` and the concrete type's fields — `... on CSVContactsExport { file { ... } }`. An `UnavailableTaskData` branch means the caller has no access to that task, or it has been deleted. |

Track a task with `getTask(id:)` polling or the `taskUpdated(id:)` subscription. Cancellation is
not on `Task`: it lives in the domain that owns the job, as with `csvContactExportCancel`, and it is
itself asynchronous — wait for a `Cancelled` status rather than assuming the call took effect.

<Callout type="warn">
  A task past its `deadline` has failed, and it may never receive a `Failed` status to say so. A
  poller that only watches `statuses` waits forever on a job that timed out.
</Callout>

## The three async patterns [#the-three-async-patterns]

| Pattern             | How completion arrives                                                                           | Examples                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| Task-tracked        | The mutation returns a `Task`; you poll `getTask` or subscribe to `taskUpdated`.                 | CSV contact export, Google Calendar sync.                                                                               |
| Domain subscription | The mutation returns the entity or a `Boolean`; completion arrives on a subscription of its own. | `csvContactImportUpdated`, `whatsAppBusinessPhoneNumberUpdated`, `metaAdsSyncStateUpdated`, `fbPagesSyncStatusUpdated`. |
| Timestamp polling   | A fire-and-forget mutation, then poll a `...LastUpdatedAt` field until it advances.              | `whatsAppEntitiesStartRefetch`, then `whatsAppEntitiesLastUpdatedAt`.                                                   |

Which one applies is written in the mutation's return type and doc comment in the bundled schema.
