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!
status: FileStatus!
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/widget?fileType=&botID= | The web widget's avatar. |
{base}/api/filestorage/upload/plugin?fileType=&botID=&pluginID= | WhatsApp template header media, and flow-builder block media, where pluginID is the block element's id. |
{base}/api/filestorage/upload/useraccount?fileType= | 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 {
Expired
NotDownloaded
DownloadInProgress
Downloaded
Failed
}On Expired, stop: do not request and do not render any other field of it. 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!
statuses: [TaskStatus!]!
completedPoints: Int!
totalPoints: Int!
data: TaskData!
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 | An interface. Select __typename and the concrete type's fields — ... on CSVContactsExport { file { ... } }. An UnavailableTaskData branch means the caller has no access to that task. |
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. |
The mutation's return type is what tells you which pattern applies: Task is the first one, and
the entity or a Boolean is the second or the third — look for a matching subscription, then fall
back to a ...LastUpdatedAt field. There is no doc comment to confirm it from, because the
schema snapshot carries names and types only.
Pagination
Relay cursors with a scalar and a page-info 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, withdrawn names, pagination, and data semantics — in full.