Skip to content
ChatfuelSDK
ReferenceChatfuel API

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.

EndpointWhat 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!
}
FieldHow to read it
statusesA 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 / totalPointsApproximate. completedPoints can exceed totalPoints, and a task can finish before the two converge, so render progress defensively.
deadlineA wall-clock cutoff, not a hint.
dataAn 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

PatternHow completion arrivesExamples
Task-trackedThe mutation returns a Task; you poll getTask or subscribe to taskUpdated.CSV contact export, Google Calendar sync.
Domain subscriptionThe mutation returns the entity or a Boolean; completion arrives on a subscription of its own.csvContactImportUpdated, whatsAppBusinessPhoneNumberUpdated, metaAdsSyncStateUpdated, fbPagesSyncStatusUpdated.
Timestamp pollingA 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.

On this page