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.
GraphQL never accepts file bytes, so every file starts as a REST upload that hands back a FileID
a mutation can then reference.
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
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
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
A long-running job — a CSV contact export, a specialist's Google Calendar sync — returns a Task.
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.
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.
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.
Pagination
Relay cursors with a scalar and a PageInfo type per connection, the direction flip on message history, where first is required, and the connections that accept pagination arguments and ignore them.
Traps
The cross-domain sharp edges of the Chatfuel API — identity and selection, schema and introspection, deprecated fields, pagination, and data semantics — in full.