Traps
The cross-domain sharp edges of the Chatfuel API — identity and selection, schema and introspection, deprecated fields, pagination, and data semantics — in full.
These bite in every domain, and none of them is visible from a type signature alone.
Identity and selection
| Trap | What to do |
|---|---|
Conversation.id is the contact id. | The backend's primary key for a conversation is Conversation.ContactID, exposed as an alias on id. Every conversationID argument takes a contact id; there is no separate conversation identity, and passing one is not a bug to fix. |
Not selecting __typename on Contact and Conversation produces errors. | A known backend bug, CFN-1289, called out in the schema's own doc comments. Select __typename on both — and, as cheap insurance, on every interface and union selection you write. |
| Interface field names are disambiguated per platform. | One field name must map to one type across every implementer, so the same concept gets different names: whatsappStatus on a WhatsApp message against plain status on a widget one, and waReferral / fbReferral / igReferral for the referral. Never assume a field from one platform's message type exists on another's. |
__typename in a mutation's variables is rejected. | The server refuses unknown input fields. If you round-trip a fetched object into a mutation input, strip __typename recursively first. |
A message's clientId must be a fresh UUID per message and unique across every client of the
account, your app and the Chatfuel dashboard included — both merge messages by clientId, so a
collision corrupts your UI and the customer's dashboard at the same time, with no error anywhere.
Message.id is nullable; clientId is the reliable key.
Schema and introspection
| Trap | What to do |
|---|---|
| Introspection is disabled in production. | Use the bundled SDL and validate operations against it before running them. |
| The bundle is not the whole dashboard schema. | Billing, Albato, A/B-experiment and debug surfaces are dropped, along with every field, argument and union member that referred to a dropped name. What is missing is missing on purpose. |
Every legacy fuelyConfig* behaviour setter is dead. | Agent name, language, greeting, message length, emoji, respond-to-sources, switch-to-human, summarize-chat, comment reply rules V1 and V2, and fuelyConfigBookingSetAIAutonomyLevel are deprecated and always return BotMigratedToNewFuelySettings. The per-scope fuelyAutomation* settings replaced them. The business-info, hours, FAQ and booking-notification setters are not deprecated — the family split. |
GetDefinedErrorCodes always errors. | It is a stub that exists only to union the error enum across subgraphs, and the schema says so. Never query it. |
| Error lists in doc comments hide under three different headings. | DefinedErrorCodes:, DefinedErrCodes: and errors: all appear. Grep for all three when hunting the codes a mutation can return. |
| Mutation casing is inconsistent. | whatsAppTextMessageSend sits next to whatsappAttachmentMessageSend; tiktokTextMessageSend returns TikTokOutTextMessage. Copy names character for character from the schema instead of deriving them. |
Deprecated, and what replaced it
| Deprecated | Use |
|---|---|
root searchContacts | bot.contactsConnection |
root countContacts | bot.contactsCount |
bot.contactChatsCount | bot.contactChatsCountV2(filter:) |
bot.contactDealsTotalsByStages | bot.contactDealsByStages(filter:) |
updateFCMTokenForCurrentMobileDevice | updateFCMTokenForCurrentDevice |
bot.commentReplyRulesV2Connection, bot.commentReplyRuleV2 | bot.fuelyAutomations. These two do not error — they return an empty connection and an empty rule. |
Pagination
| Trap | What to do |
|---|---|
Bot.whatsAppTemplates accepts first, after and before and ignores all three. | It returns the full list every time, and the field is nullable. The same absence, more honestly stated, applies to goodsCatalog, specialists, contactScopes, members, invites, bookingsV2, Workspace.bots and currentUser.workspaces, which have no pagination arguments at all. |
| Message history flips direction. | No cursor or after gives newest-first descending; before gives ascending. first is required on the contact-ish connections and optional on messages. The rules are on the pagination page. |
Data semantics
| Trap | What to do |
|---|---|
| "Deleted or inaccessible" is modelled three different ways. | Union branches (DeletedGoodsService, DeletedSpecialist), boolean flags (PublicUserAccount.isUnknown, TikTokPost.isUnknown — when true, ignore every other field), and stub types (UnavailableContact, which means no permission and holds empty data; UnavailableTaskData). Handle all three; there is no single shape to check. |
File.status == Expired means the file is gone. | Do not request and do not render any other field of it. |
| Datetime attribute values are not RFC3339. | An attribute whose AttributeDataType is datetime carries a millisecond-timestamp string, like "1720456863000". Only the Time scalar uses RFC3339. |
Task.statuses is a history. | The current status is the entry with the latest startedAt, and a task past its deadline has failed even with no Failed status. See files and tasks. |
contactsCount and contactsTotalCount answer different questions. | contactsCount respects the caller's assignee visibility restrictions and is what a contacts table should show; contactsTotalCount ignores them and is what a broadcast size estimate wants. Pick deliberately — both return an Int! and neither says which it is. |
The transport-level traps live with their subjects: an Unauthorized arriving inside an HTTP 200
and a subgraph code nested one level down are on errors; a
socket that replays nothing on reconnect and the 25-requests-a-second ceiling are on
transport and auth; uploads never going through
GraphQL is on files and tasks.
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.
The barrels
The eighteen sub-barrels of the design system, what is in each, and the four rules that keep a vendored copy working.