{
  "schema_version": "1.0",
  "name": "ChatFood Flow API",
  "name_for_model": "chatfood_flow_api",
  "description_for_human": "Build and publish conversation flows for Facebook Messenger, Instagram Direct and Telegram pages, segment leads, schedule broadcasts and journeys, and read delivery metrics.",
  "description_for_model": "Control plane of ChatFood, public API v1 (every route under /v1). Use it to: list the caller's accounts and pages; create, validate and publish conversation flows (/v1/schemas) whose `script` is a JSON string of routes and nodes executed by the ChatFood bot; trigger immediate or scheduled broadcasts of a published flow to the leads of one or more pages with recency, tag and subscriber segmentation; create multi-step journeys; list leads and tags; read funnel metrics through GraphQL. Read /llms-full.txt before building a flow script: it documents node types, buttons, variables, awaitReply capture, per-provider support and limits. Enums are integers. Pages are always addressed by their GUID. Pass accountId explicitly when the credential reaches more than one account.",
  "base_url": "https://api.chatfood.app",
  "documentation": {
    "llms_txt": "https://api.chatfood.app/llms.txt",
    "llms_full_txt": "https://api.chatfood.app/llms-full.txt",
    "openapi_agents": "https://api.chatfood.app/swagger/agents/swagger.json",
    "openapi": "https://api.chatfood.app/swagger/v1/swagger.json",
    "recipes": "https://api.chatfood.app/llms-full.txt#16-task-recipes",
    "swagger_ui": "https://api.chatfood.app/swagger",
    "graphql": "https://api.chatfood.app/graphql",
    "web_app": "https://chatfood.app",
    "reference": "https://api.chatfood.app/docs",
    "postman": "https://api.chatfood.app/docs/postman.json"
  },
  "auth": {
    "preferred": "api_token",
    "schemes": [
      {
        "type": "api_token",
        "description": "Long-lived token bound to a user, to a set of scopes and optionally to one account. Created by a logged-in user at POST /v1/api-tokens; the plaintext (prefix `cf_live_`) is returned once. Each request is limited to the intersection of the token scopes with what the owner can do in each account at that moment. Never carries admin roles and never reaches admin, internal or token-management routes. Revocable at DELETE /v1/api-tokens/{tokenId}; revoked or expired tokens get 401 immediately. Rate limited per token (429 with Retry-After).",
        "token_prefix": "cf_live_",
        "token_pattern": "cf_live_[A-Za-z0-9_-]{43}",
        "in": [
          "header"
        ],
        "headers": [
          {
            "name": "Authorization",
            "format": "Bearer cf_live_..."
          },
          {
            "name": "X-Api-Key",
            "format": "cf_live_..."
          }
        ],
        "create": {
          "method": "POST",
          "path": "/v1/api-tokens",
          "requires": "jwt",
          "body": {
            "name": "string",
            "accountId": "guid?",
            "expiresAt": "date-time?",
            "scopes": [
              "area:read | area:write"
            ]
          }
        },
        "introspect": {
          "method": "GET",
          "path": "/v1/me"
        },
        "list": {
          "method": "GET",
          "path": "/v1/api-tokens",
          "requires": "jwt"
        },
        "revoke": {
          "method": "DELETE",
          "path": "/v1/api-tokens/{tokenId}",
          "requires": "jwt"
        }
      },
      {
        "type": "jwt",
        "description": "18-hour user session. HS256 bearer token from POST /Auth with the user's email and password. Not revocable; re-login on 401.",
        "in": [
          "header"
        ],
        "headers": [
          {
            "name": "Authorization",
            "format": "Bearer <jwt>"
          }
        ],
        "obtain": {
          "method": "POST",
          "path": "/Auth",
          "body": {
            "username": "email",
            "password": "string"
          },
          "response": {
            "token": "jwt"
          }
        }
      }
    ],
    "rate_limit": {
      "requests": 120,
      "window_seconds": 60,
      "partition": "token",
      "scope": "per API instance",
      "on_exceed": "429 with Retry-After (seconds)"
    },
    "tenancy": "A user belongs to one or more accounts. The account comes from the credential; when it reaches more than one, pass ?accountId=<guid>: reads without it span every account, writes that create a resource answer 400 asking for it. Account-scoped API tokens see only their account.",
    "scopes": {
      "format": "area:access, where access is read or write and write implies read",
      "areas": {
        "pages": "pages, connection history, Meta and Telegram page connection, ice breakers, page variables, alerts",
        "leads": "leads, lead tags, deleting the leads of a page",
        "flows": "flows (/v1/schemas), utility templates, Meta message templates, media upload for flows, test send",
        "broadcasts": "broadcasts and their executions",
        "journeys": "journeys",
        "metrics": "alerts summary, flow metrics, send time suggestion and the GraphQL endpoint (broadcastCalendar also needs broadcasts:read, profileCalendar also needs leads:read)",
        "websites": "website domains, slugs and websites",
        "pixels": "Meta datasets (conversion pixels)",
        "payments": "reserved for Pix reports (not published yet)",
        "account": "account settings, UTM and language slugs, Meta apps, notifications, categories and variable groups",
        "users": "listing account members; adding and removing members is not available to API tokens",
        "billing": "billing overview, invoices, profile and usage"
      },
      "rules": [
        "GET needs read, other verbs need write, except where a route declares otherwise",
        "A route without a declared area is not available to API tokens (403)",
        "Admin, internal and token-management routes are never available to API tokens",
        "A token never exceeds its owner: missing access in an account hides that account from the request",
        "403 responses from the scope check carry a problem+json detail naming the missing scope",
        "Every operation of the agents OpenAPI document declares its scope in x-required-scope ('any' = every active token)"
      ]
    }
  },
  "providers": [
    {
      "id": 1,
      "name": "Facebook",
      "page_reference": "Facebook page id (informative; the API takes the page GUID)",
      "lead_reference": "PSID",
      "connect": "POST /v1/meta/pages (requires Facebook Login in a browser)"
    },
    {
      "id": 2,
      "name": "Instagram",
      "page_reference": "Instagram professional account id (informative)",
      "lead_reference": "IGSID",
      "connect": "POST /v1/meta/pages (requires Facebook Login in a browser)"
    },
    {
      "id": 3,
      "name": "Telegram",
      "page_reference": "bot id (informative)",
      "lead_reference": "chat id",
      "connect": "POST /v1/telegram/pages { botToken }"
    }
  ],
  "capabilities": [
    {
      "id": "discover_scope",
      "scopes": [
        "any",
        "account",
        "pages"
      ],
      "summary": "Find out who the credential is and which accounts and pages it can use. /v1/me works with any token; /v1/accounts needs account:read; /v1/pages needs pages:read.",
      "endpoints": [
        {
          "method": "GET",
          "path": "/v1/me",
          "scopes": [
            "any"
          ]
        },
        {
          "method": "GET",
          "path": "/v1/accounts",
          "scopes": [
            "account"
          ]
        },
        {
          "method": "GET",
          "path": "/v1/pages",
          "scopes": [
            "pages"
          ],
          "query": [
            "fields",
            "limit",
            "cursor",
            "sort"
          ]
        },
        {
          "method": "GET",
          "path": "/v1/pages/{pageId}",
          "scopes": [
            "pages"
          ]
        }
      ]
    },
    {
      "id": "manage_pages",
      "scopes": [
        "pages",
        "metrics"
      ],
      "summary": "Connect Telegram bots, adjust page settings, variables, ice breakers and read health.",
      "endpoints": [
        {
          "method": "POST",
          "path": "/v1/telegram/pages",
          "query": [
            "accountId"
          ]
        },
        {
          "method": "GET",
          "path": "/v1/telegram/pages/{pageId}/channels"
        },
        {
          "method": "PATCH",
          "path": "/v1/pages/{pageId}"
        },
        {
          "method": "GET",
          "path": "/v1/pages/{pageId}/variables"
        },
        {
          "method": "POST",
          "path": "/v1/pages/{pageId}/variables"
        },
        {
          "method": "PUT",
          "path": "/v1/pages/{pageId}/variables"
        },
        {
          "method": "GET",
          "path": "/v1/meta/pages/{pageId}/ice-breakers"
        },
        {
          "method": "PUT",
          "path": "/v1/meta/pages/{pageId}/ice-breakers"
        },
        {
          "method": "GET",
          "path": "/v1/alerts/summary"
        },
        {
          "method": "GET",
          "path": "/v1/alerts"
        }
      ]
    },
    {
      "id": "build_flow",
      "scopes": [
        "flows",
        "leads"
      ],
      "summary": "Create, validate, publish, copy and test conversation flows. The `script` field follows the contract in /llms-full.txt section 6.",
      "doc": "https://api.chatfood.app/llms-full.txt#6-the-flow-script-contract-what-the-bot-executes",
      "endpoints": [
        {
          "method": "POST",
          "path": "/v1/schemas",
          "query": [
            "accountId"
          ]
        },
        {
          "method": "PUT",
          "path": "/v1/schemas/{schemaId}"
        },
        {
          "method": "PATCH",
          "path": "/v1/schemas/{schemaId}",
          "body": {
            "status": "1 publish, 0 draft"
          }
        },
        {
          "method": "GET",
          "path": "/v1/schemas/{schemaId}"
        },
        {
          "method": "GET",
          "path": "/v1/schemas"
        },
        {
          "method": "POST",
          "path": "/v1/schemas/validate-variables"
        },
        {
          "method": "POST",
          "path": "/v1/schemas/{schemaId}/copy"
        },
        {
          "method": "DELETE",
          "path": "/v1/schemas/{schemaId}"
        },
        {
          "method": "POST",
          "path": "/v1/schemas/{schemaId}/test",
          "body": {
            "pageId": "guid",
            "leadId": "guid"
          }
        },
        {
          "method": "GET",
          "path": "/v1/schemas/{schemaId}/utility-templates"
        },
        {
          "method": "POST",
          "path": "/v1/uploads",
          "body": {
            "folder": "string",
            "extension": "string"
          }
        },
        {
          "method": "GET",
          "path": "/v1/tags"
        }
      ],
      "script_contract": {
        "entry_route": "WELCOME",
        "route_shape": "array of nodes, or {\"MESSAGES\": [nodes]}",
        "message_node_types": [
          "text",
          "buttons_menu",
          "simple_menu",
          "attachment",
          "location",
          "typing",
          "share_feed",
          "recurring",
          "utility_template"
        ],
        "action_node_types": [
          "delay",
          "goTo",
          "random",
          "hasTagConditional",
          "addTag",
          "removeTag",
          "unsubscribe",
          "handover_protocol",
          "pix"
        ],
        "action_nodes_require": {
          "format": "action"
        },
        "routing_fields": [
          "redirect",
          "redirectTrue",
          "redirectFalse",
          "redirectFallback",
          "option[].action",
          "options[].action",
          "awaitReply.onInvalid.route",
          "awaitReply.timeout.route"
        ],
        "button": {
          "type": [
            "redirect",
            "url"
          ],
          "required": [
            "title"
          ],
          "postback_target": "action (route name)",
          "url": [
            "url",
            "urls[{url,weight}]"
          ],
          "telegram_only": [
            "style"
          ]
        },
        "variables": {
          "syntax": "{{NAME}}",
          "builtins": [
            "NAME",
            "FIRST_NAME",
            "EMAIL",
            "ORDER_ID",
            "DATE_NOW",
            "DELIVERY_AT",
            "PROFILE_ID",
            "LEAD_ID",
            "PSID"
          ],
          "captured": "{{<awaitReply.saveAs>}} stored as PROVIDED_<SAVEAS>"
        },
        "await_reply": {
          "required": [
            "enabled=true",
            "saveAs"
          ],
          "expects": [
            "any",
            "text",
            "number",
            "email",
            "phone",
            "cpf",
            "date",
            "attachment",
            "location"
          ],
          "default_timeout_seconds": 300
        },
        "limits": {
          "text": 2000,
          "template_text": 640,
          "button_title": 20,
          "quick_replies": 13,
          "buttons_per_template": 3,
          "carousel_cards": 10,
          "card_title": 80,
          "telegram_text": 4096,
          "telegram_button_title": 64
        }
      }
    },
    {
      "id": "send_broadcast",
      "scopes": [
        "broadcasts",
        "metrics"
      ],
      "summary": "Send a published flow to the leads of one or more pages, now or scheduled (page-local wall clock), with recency, tag and subscriber segmentation; then monitor per-page executions.",
      "doc": "https://api.chatfood.app/llms-full.txt#7-broadcasts",
      "endpoints": [
        {
          "method": "POST",
          "path": "/v1/broadcasts"
        },
        {
          "method": "GET",
          "path": "/v1/broadcasts/{broadcastId}"
        },
        {
          "method": "GET",
          "path": "/v1/broadcasts/{broadcastId}/executions"
        },
        {
          "method": "GET",
          "path": "/v1/broadcasts"
        },
        {
          "method": "GET",
          "path": "/v1/broadcasts/overview"
        },
        {
          "method": "GET",
          "path": "/v1/broadcasts/executions"
        },
        {
          "method": "PUT",
          "path": "/v1/broadcasts/{broadcastId}"
        },
        {
          "method": "PATCH",
          "path": "/v1/broadcasts/{broadcastId}"
        },
        {
          "method": "POST",
          "path": "/v1/broadcasts/{broadcastId}/cancel"
        },
        {
          "method": "POST",
          "path": "/v1/broadcasts/{broadcastId}/executions/{executionId}/reprocess"
        },
        {
          "method": "POST",
          "path": "/v1/broadcasts/delivery-preview"
        },
        {
          "method": "GET",
          "path": "/v1/schemas/{schemaId}/metrics",
          "query": [
            "broadcastId"
          ]
        }
      ],
      "defaults": {
        "type": 5,
        "notificationType": 1,
        "recencyFilter": 0,
        "tagMatchMode": 1
      },
      "rules": [
        "schemaId must be a Published flow",
        "type 6 (All) only for Telegram pages",
        "recency filters apply to types 3, 4 and 5 only; Custom needs bounds in 1..2160 hours",
        "scheduledTo is interpreted in each page's timezone"
      ]
    },
    {
      "id": "manage_journeys",
      "scopes": [
        "journeys"
      ],
      "summary": "Create multi-step drip sequences of published flows and activate, pause or archive them.",
      "endpoints": [
        {
          "method": "POST",
          "path": "/v1/journeys",
          "query": [
            "accountId"
          ]
        },
        {
          "method": "PUT",
          "path": "/v1/journeys/{journeyId}"
        },
        {
          "method": "PATCH",
          "path": "/v1/journeys/{journeyId}/status",
          "body": {
            "status": "2 Active | 3 Paused | 4 Archived"
          }
        },
        {
          "method": "GET",
          "path": "/v1/journeys"
        },
        {
          "method": "GET",
          "path": "/v1/journeys/{journeyId}"
        },
        {
          "method": "GET",
          "path": "/v1/journeys/{journeyId}/leads-count"
        },
        {
          "method": "GET",
          "path": "/v1/journeys/{journeyId}/funnel"
        },
        {
          "method": "POST",
          "path": "/v1/journeys/{journeyId}/copy"
        }
      ]
    },
    {
      "id": "read_leads",
      "scopes": [
        "leads"
      ],
      "summary": "List leads of a page with their tags and captured properties.",
      "endpoints": [
        {
          "method": "GET",
          "path": "/v1/leads",
          "query": [
            "pageIds",
            "search",
            "tags",
            "provider",
            "limit",
            "cursor",
            "sort"
          ]
        },
        {
          "method": "GET",
          "path": "/v1/leads/{leadId}"
        },
        {
          "method": "GET",
          "path": "/v1/tags"
        },
        {
          "method": "POST",
          "path": "/v1/tags",
          "query": [
            "accountId"
          ]
        }
      ]
    },
    {
      "id": "read_metrics",
      "scopes": [
        "metrics"
      ],
      "summary": "Funnel and delivery metrics per flow, broadcast, page and account.",
      "graphql": {
        "url": "https://api.chatfood.app/graphql",
        "queries": [
          "flowMetrics",
          "leadFunnel",
          "dashboard",
          "broadcastCalendar",
          "broadcastCalendarSummary",
          "profileCalendar",
          "analytics",
          "websiteMetrics"
        ]
      },
      "endpoints": [
        {
          "method": "GET",
          "path": "/v1/alerts/summary"
        },
        {
          "method": "GET",
          "path": "/v1/broadcasts/overview"
        },
        {
          "method": "GET",
          "path": "/v1/schemas/{schemaId}/metrics",
          "query": [
            "broadcastId"
          ]
        },
        {
          "method": "GET",
          "path": "/v1/journeys/{journeyId}/funnel"
        },
        {
          "method": "GET",
          "path": "/v1/pages/{pageId}/send-time-suggestion"
        }
      ]
    },
    {
      "id": "manage_pixels",
      "scopes": [
        "pixels",
        "account"
      ],
      "summary": "Register the account's Meta datasets (pixels) for conversion attribution, covering every page or only some categories. The Conversions API token is write only (responses carry hasAccessToken).",
      "doc": "https://api.chatfood.app/llms-full.txt#14-conversion-datasets-pixels",
      "endpoints": [
        {
          "method": "GET",
          "path": "/v1/meta/datasets",
          "query": [
            "accountId"
          ]
        },
        {
          "method": "POST",
          "path": "/v1/meta/datasets",
          "query": [
            "accountId"
          ],
          "body": {
            "name": "string",
            "metaDatasetId": "digits",
            "accessToken": "string",
            "testEventCode": "string?",
            "websiteEventName": "Lead (default) | CompleteRegistration | Contact | Schedule | SubmitApplication | ViewContent | FindLocation | Search",
            "messagingEventName": "LeadSubmitted (default) | QualifiedLead | ViewContent | InitiateCheckout | AddToCart | RatingProvided | ReviewProvided",
            "isActive": "bool",
            "appliesToAllPages": "bool",
            "categoryIds": [
              "guid"
            ]
          }
        },
        {
          "method": "GET",
          "path": "/v1/meta/datasets/{datasetId}",
          "query": [
            "accountId"
          ]
        },
        {
          "method": "PATCH",
          "path": "/v1/meta/datasets/{datasetId}",
          "query": [
            "accountId"
          ]
        },
        {
          "method": "DELETE",
          "path": "/v1/meta/datasets/{datasetId}",
          "query": [
            "accountId"
          ]
        },
        {
          "method": "GET",
          "path": "/v1/categories",
          "scopes": [
            "account"
          ]
        }
      ]
    },
    {
      "id": "manage_websites",
      "scopes": [
        "websites"
      ],
      "summary": "Manage the account's website domains used by flow buttons: language pattern, weights, UTM values, slugs and websites. PUT on a domain replaces it entirely; send back every field read.",
      "doc": "https://api.chatfood.app/llms-full.txt#15-websites-websites",
      "endpoints": [
        {
          "method": "GET",
          "path": "/v1/website-domains",
          "query": [
            "accountId"
          ]
        },
        {
          "method": "GET",
          "path": "/v1/website-domains/{domainId}"
        },
        {
          "method": "POST",
          "path": "/v1/website-domains",
          "query": [
            "accountId"
          ]
        },
        {
          "method": "PUT",
          "path": "/v1/website-domains/{domainId}"
        },
        {
          "method": "DELETE",
          "path": "/v1/website-domains/{domainId}"
        },
        {
          "method": "GET",
          "path": "/v1/website-domains/{domainId}/slugs"
        },
        {
          "method": "PUT",
          "path": "/v1/website-domains/{domainId}/slugs"
        },
        {
          "method": "GET",
          "path": "/v1/website-domains/{domainId}/websites"
        },
        {
          "method": "POST",
          "path": "/v1/website-domains/validate-script",
          "scopes": [
            "websites:read"
          ]
        }
      ]
    }
  ],
  "not_supported": [
    "Outbound webhooks or event subscriptions (poll instead)",
    "Sending a free-form message to a lead outside a flow",
    "Creating leads",
    "Connecting Facebook/Instagram pages without a human completing Facebook Login",
    "GraphQL mutations",
    "OAuth client credentials",
    "Pix reports (not published yet)"
  ],
  "errors": {
    "format": "application/problem+json {type, title, status, detail}",
    "400": "domain validation, or accountId missing when the credential reaches several accounts; fix the payload, do not retry unchanged",
    "401": "credential missing, malformed, expired or revoked; stop and ask for a new token",
    "403_scope": "scope missing (detail names it, e.g. 'broadcasts:write') or route not available to API tokens",
    "403_account": "resource of an account the credential cannot use",
    "404": "missing or not visible to this account",
    "409": "conflict; read detail",
    "429": "over the per token limit; wait Retry-After seconds"
  },
  "best_practices": [
    "Call GET /v1/me first and pass accountId explicitly when the credential reaches more than one account",
    "Request the fewest scopes; write implies read",
    "One token per account and purpose, with expiresAt; revoke when done",
    "Never log, print or put the token in a URL",
    "Confirm with a human before writes that reach leads (broadcasts, journey activation, flow publication)",
    "POST /v1/broadcasts is not idempotent: after a timeout, list /v1/broadcasts before retrying",
    "Walk lists with nextCursor until it is null; never build cursors",
    "Back off on 429 using Retry-After; retry writes only when the first attempt is confirmed absent"
  ],
  "conventions": {
    "base_path": "/v1",
    "urls": "lower case, hyphens in compound names, plural collections, child under its owner (at most two levels)",
    "json": "camelCase",
    "enums": "integers",
    "dates": "ISO 8601, UTC unless noted (broadcast scheduledTo is page-local)",
    "pages": "always addressed by GUID (pageId)",
    "pagination": "?limit=&cursor=&sort= (-field for descending); response {items, nextCursor}, plus total when the list counts its filter (GET /v1/schemas only with ?includeTotal=true)",
    "updates": "PUT replaces the whole resource; PATCH changes only the fields sent: a missing field is kept, an explicit null clears an optional field and is a 400 on a required one (PATCH /v1/pages/{pageId} clears with \"\" or the empty GUID; PATCH /v1/meta/apps/{appId} keeps on null)",
    "batches": "PUT on the collection upserts; DELETE on the collection with ?ids= removes and answers {deleted: n} for variables, slugs and UTM",
    "errors": "application/problem+json, see the errors object",
    "partial_success": "skippedPageIds lists pages dropped from a broadcast, journey or flow",
    "versioning": "/v1 only changes in ways that keep existing clients working; a breaking change ships as /v2"
  },
  "contact": {
    "web": "https://chatfood.app"
  }
}
