{"openapi":"3.0.3","info":{"title":"floGPT Organization API","version":"1.0.0","description":"Configure chat and call widgets, manage CRM records, wire up integrations and\noutbound triggers, and read conversations, call logs, call centre history and\nanalytics for one floGPT organization.\n\nAPI keys are organization-scoped and can be created by an owner or admin in\n**Settings → Security → API Keys**. Send a key as `Authorization: Bearer <key>`\nor in the `x-api-key` header. Keys are shown only once when created.\n\nEvery operation requires exactly one scope, published below as\n`x-required-scope`. Scopes are not hierarchical — `:write` does not imply\n`:read`.\n\nRead requests default to 600 per minute and writes (POST/PUT/PATCH/DELETE) to\n120 per minute, overridable per key. Use the `X-RateLimit-Limit`,\n`X-RateLimit-Remaining` and `X-RateLimit-Reset` response headers to monitor\nthe active limit; a `429` also carries `Retry-After`.\n\nA resource belonging to another organization returns `404`, not `403`, so a\nkey cannot probe for identifiers it does not own.\n"},"servers":[{"url":"/api/v1","description":"Current floGPT deployment"}],"tags":[{"name":"System"},{"name":"Chat widgets"},{"name":"Call widgets"},{"name":"Call pipelines"},{"name":"Knowledge bases"},{"name":"Phone numbers"},{"name":"CRM contacts"},{"name":"CRM tickets"},{"name":"Conversations"},{"name":"Call logs"},{"name":"Call centre","description":"Calls handled by a human rep on the softphone, as opposed to the AI voice\nagent under **Call logs**. An API key authenticates as the organization\nrather than as a member of it, so these reads are always floor-wide — use\n`agentId` to narrow to one rep.\n"},{"name":"Analytics"},{"name":"Meta Lead Ads"},{"name":"Outbound triggers"},{"name":"Data collections"},{"name":"Email integrations"},{"name":"Organization","description":"The organization's own configuration. Scoped tightly on purpose: billing,\nusage, ownership, membership and the role-permission policy are not\nreachable at any scope, and stay behind a dashboard session.\n"},{"name":"Team"},{"name":"Workflows"},{"name":"Integrations","description":"Channel integrations across every platform. Credentials are write-only —\nreads report each one as configured or not, never its value. Email is also\nserved by **Email integrations**, which is the canonical route for it.\n"},{"name":"Calendars"},{"name":"Billing","description":"Read-only. There is deliberately no `billing:write` scope: plan and\npayment-method changes move money and are only possible in the dashboard.\n"},{"name":"Setup"},{"name":"Diagnostics","description":"Read-only self-troubleshooting. Every check probes the workspace and\nreports findings; none of them can change configuration, which is why\nthere is no `diagnostics:write` scope. Both routes are GET, including the\none that runs a suite — running a diagnostic *is* a read.\n"},{"name":"Event subscriptions","description":"Push delivery for floGPT events. Subscribe a URL and floGPT POSTs each\nmatching event to it, signed with the subscription's own secret, instead\nof the client polling for changes.\n"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"floGPT organization API key","description":"Either an organization API key (`fg_…`) or an OpenID Connect access\ntoken issued by floGPT's provider at `/oidc`. To obtain the latter,\nrequest `resource=<backend origin>/api/v1` along with the `/api/v1`\nscopes you need; the granted scopes are still bounded by the ones the\nclient registration was approved for.\n"},"apiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key"}},"parameters":{"ResourceId":{"in":"path","name":"id","required":true,"description":"Resource identifier. Where a resource also has a public identifier —\n`widgetId`, `callWidgetId`, or a carrier call sid — either form is\naccepted.\n","schema":{"type":"string"}},"Page":{"in":"query","name":"page","schema":{"type":"integer","minimum":1,"default":1}},"Limit":{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},"From":{"in":"query","name":"from","description":"Inclusive beginning of the reporting period.","schema":{"type":"string","format":"date-time"}},"To":{"in":"query","name":"to","description":"Inclusive end of the reporting period.","schema":{"type":"string","format":"date-time"}}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, invalid, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The API key does not include the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"The resource does not exist in the API key's organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"The API key exceeded its read or write rate limit.","headers":{"Retry-After":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The request cannot be applied in the resource's current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","example":"forbidden"},"message":{"type":"string","example":"API key is missing the required scope."},"details":{"nullable":true}}}}},"Resource":{"type":"object","description":"Resource fields vary by endpoint. Identifiers are serialized as strings.","additionalProperties":true},"ResourceInput":{"type":"object","additionalProperties":true},"ResourceList":{"type":"object","description":"The envelope every paginated list endpoint returns.","required":["data","page","limit","total"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Resource"}},"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"description":"Matching records across all pages.","type":"integer"}}},"ActionResult":{"type":"object","additionalProperties":true},"Lead":{"type":"object","description":"One row of a data collection worked as a lead list. The pipeline fields\nlive on the row itself; `fields` is the row's own columns, whatever the\nworkspace named them. `name`, `phone`, `email` and `company` are read out\nof `fields` on a best-effort basis and are null when no column matched —\n`fields` is always the authoritative copy.\n","properties":{"id":{"type":"string"},"rowNumber":{"type":"integer"},"name":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"company":{"type":"string","nullable":true},"status":{"type":"string","nullable":true,"enum":["new","contacted","callback","won","lost",null]},"potential":{"type":"string","nullable":true,"enum":["hot","warm","cold",null]},"followUpAt":{"type":"string","format":"date-time","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"optedOut":{"description":"Suppresses every automated outbound send for this lead.","type":"boolean"},"assignedToUserId":{"type":"string","nullable":true},"statusEnteredAt":{"type":"string","format":"date-time","nullable":true},"firstContactedAt":{"type":"string","format":"date-time","nullable":true},"lastContactedAt":{"type":"string","format":"date-time","nullable":true},"fields":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"LeadInput":{"type":"object","description":"A blank or omitted value means \"leave whatever is there\" rather than\n\"clear it\", so a lead pulled from the API can be edited and pushed back\nwithout wiping the fields the caller did not touch. Unrecognised\n`status` and `potential` values are reported in `warnings` and skipped\nrather than failing the row.\n","properties":{"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"company":{"type":"string"},"status":{"type":"string","enum":["new","contacted","callback","won","lost"]},"potential":{"type":"string","enum":["hot","warm","cold"]},"followUpAt":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"type":"string"}},"optedOut":{"type":"boolean"},"fields":{"description":"Extra columns, keyed by column name. Columns the list does not have\nyet are added to it, with their type inferred from the values in the\nrequest. A key that collides with one of the named fields above is\nignored in favour of the named one.\n","type":"object","additionalProperties":true}}},"LeadImportResult":{"type":"object","description":"Per-request tally. Rows are reported, not rejected wholesale.","properties":{"created":{"type":"integer"},"updated":{"type":"integer"},"skipped":{"description":"Rows carrying nothing usable, or repeating a match key from earlier in the same request.","type":"integer"},"columnsAdded":{"description":"Columns this request added to the list.","type":"array","items":{"type":"string"}},"columnsIgnored":{"description":"Fields recognised as server-derived and therefore not importable.","type":"array","items":{"type":"string"}},"matchedOn":{"description":"The column that decided whether a row was an update. Null when every row was inserted.","type":"string","nullable":true},"warnings":{"type":"array","items":{"type":"string"}}}}},"requestBodies":{"ResourceInput":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceInput"}}}}}},"paths":{"/health":{"get":{"tags":["System"],"summary":"Check API availability","operationId":"getApiHealth","security":[],"responses":{"200":{"description":"The API is available.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"version":{"type":"string","example":"v1"}}}}}}}}},"/openapi.json":{"get":{"tags":["System"],"summary":"Get this specification as JSON","operationId":"getApiSpec","security":[],"responses":{"200":{"description":"The OpenAPI document.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/me":{"get":{"tags":["System"],"summary":"Identify the calling API key","description":"Returns the organization the key belongs to and the scopes it holds.\n\nRequires authentication but no scope, so a client can verify a key\nwithout being granted read access to a resource it does not otherwise\nneed.\n","operationId":"getApiIdentity","responses":{"200":{"description":"The calling key's identity.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"organizationId":{"type":"string"},"keyId":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/chat-widgets":{"get":{"tags":["Chat widgets"],"summary":"List chat widgets","operationId":"listChatWidgets","x-required-scope":"widgets:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Chat widget page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"tags":["Chat widgets"],"summary":"Create a chat widget","operationId":"createChatWidget","x-required-scope":"widgets:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created chat widget","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/chat-widgets/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Chat widgets"],"summary":"Get a chat widget","operationId":"getChatWidget","x-required-scope":"widgets:read","responses":{"200":{"description":"Chat widget","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Chat widgets"],"summary":"Update a chat widget","operationId":"updateChatWidget","x-required-scope":"widgets:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated chat widget"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Chat widgets"],"summary":"Delete a chat widget","operationId":"deleteChatWidget","x-required-scope":"widgets:write","responses":{"204":{"description":"Chat widget deleted"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/chat-widgets/{id}/embed-code":{"get":{"tags":["Chat widgets"],"summary":"Get chat widget embed code and test URL","operationId":"getChatWidgetEmbedCode","x-required-scope":"widgets:read","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Embed code"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/chat-widgets/{id}/attach-knowledge-base":{"post":{"tags":["Chat widgets"],"summary":"Attach a knowledge base to a chat widget","operationId":"attachChatWidgetKnowledgeBase","x-required-scope":"widgets:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated chat widget"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/call-widgets":{"get":{"tags":["Call widgets"],"summary":"List call widgets","operationId":"listCallWidgets","x-required-scope":"call_widgets:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Call widget page"}}},"post":{"tags":["Call widgets"],"summary":"Create a call widget","operationId":"createCallWidget","x-required-scope":"call_widgets:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created call widget"}}}},"/call-widgets/from-blueprint":{"post":{"tags":["Call widgets"],"summary":"Create a call widget from a blueprint","operationId":"createCallWidgetFromBlueprint","x-required-scope":"call_widgets:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created call widget"}}}},"/call-widgets/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Call widgets"],"summary":"Get a call widget","operationId":"getCallWidget","x-required-scope":"call_widgets:read","responses":{"200":{"description":"Call widget"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Call widgets"],"summary":"Update a call widget","operationId":"updateCallWidget","x-required-scope":"call_widgets:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated call widget"}}},"delete":{"tags":["Call widgets"],"summary":"Delete a call widget","operationId":"deleteCallWidget","x-required-scope":"call_widgets:write","responses":{"204":{"description":"Call widget deleted"}}}},"/call-widgets/{id}/toggle":{"post":{"tags":["Call widgets"],"summary":"Enable or disable a call widget","operationId":"toggleCallWidget","x-required-scope":"call_widgets:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated call widget"}}}},"/call-widgets/{id}/attach-pipeline":{"post":{"tags":["Call widgets"],"summary":"Attach a pipeline to a call widget","operationId":"attachCallWidgetPipeline","x-required-scope":"call_widgets:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated call widget"}}}},"/call-widgets/{id}/test-call":{"post":{"tags":["Call widgets"],"summary":"Place a test call from a call widget","description":"Queues one outbound call to `to` (E.164) on the widget's trigger\ncampaign. Accepted asynchronously — the call is placed by the queue, so\na `202` means it was enqueued, not answered.\n","operationId":"createCallWidgetTestCall","x-required-scope":"call_widgets:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to"],"properties":{"to":{"type":"string","description":"Destination in E.164 format.","example":"+14155551234"}}}}}},"responses":{"202":{"description":"Test call queued"},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/call-pipelines":{"get":{"tags":["Call pipelines"],"summary":"List call pipelines","operationId":"listCallPipelines","x-required-scope":"call_widgets:read","responses":{"200":{"description":"Call pipeline page"}}},"post":{"tags":["Call pipelines"],"summary":"Create a call pipeline","operationId":"createCallPipeline","x-required-scope":"call_widgets:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created call pipeline"}}}},"/call-pipelines/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Call pipelines"],"summary":"Get a call pipeline","operationId":"getCallPipeline","x-required-scope":"call_widgets:read","responses":{"200":{"description":"Call pipeline"}}},"patch":{"tags":["Call pipelines"],"summary":"Update a call pipeline","operationId":"updateCallPipeline","x-required-scope":"call_widgets:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated call pipeline"}}}},"/knowledge-bases":{"get":{"tags":["Knowledge bases"],"summary":"List knowledge bases","operationId":"listKnowledgeBases","x-required-scope":"knowledge:read","responses":{"200":{"description":"Knowledge base page"}}},"post":{"tags":["Knowledge bases"],"summary":"Create a knowledge base","operationId":"createKnowledgeBase","x-required-scope":"knowledge:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created knowledge base"}}}},"/knowledge-bases/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Knowledge bases"],"summary":"Get a knowledge base","operationId":"getKnowledgeBase","x-required-scope":"knowledge:read","responses":{"200":{"description":"Knowledge base"}}},"patch":{"tags":["Knowledge bases"],"summary":"Update a knowledge base","operationId":"updateKnowledgeBase","x-required-scope":"knowledge:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated knowledge base"}}}},"/knowledge-bases/{id}/documents":{"post":{"tags":["Knowledge bases"],"summary":"Add a document to a knowledge base","operationId":"addKnowledgeBaseDocument","x-required-scope":"knowledge:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created knowledge document"}}}},"/knowledge-bases/{id}/sync":{"post":{"tags":["Knowledge bases"],"summary":"Start knowledge base synchronization","operationId":"syncKnowledgeBase","x-required-scope":"knowledge:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Synchronization result"}}}},"/phone-numbers":{"get":{"tags":["Phone numbers"],"summary":"List organization phone numbers","operationId":"listPhoneNumbers","x-required-scope":"call_widgets:read","responses":{"200":{"description":"Phone number page"}}}},"/phone-numbers/{id}/link":{"post":{"tags":["Phone numbers"],"summary":"Link a phone number to a call widget","operationId":"linkPhoneNumber","x-required-scope":"call_widgets:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Linked phone number"}}}},"/phone-numbers/{id}/unlink":{"post":{"tags":["Phone numbers"],"summary":"Unlink a phone number from its call widget","operationId":"unlinkPhoneNumber","x-required-scope":"call_widgets:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Unlinked phone number"}}}},"/phone-numbers/requests":{"get":{"tags":["Phone numbers"],"summary":"List phone number provisioning requests","operationId":"listPhoneNumberRequests","x-required-scope":"phone_numbers:read","responses":{"200":{"description":"Request page"}}},"post":{"tags":["Phone numbers"],"summary":"Request a phone number","description":"Numbers cannot be purchased through this API. Self-serve provisioning\ncovers only a handful of countries; everywhere else needs KYC or carrier\npaperwork, so this files a request a floGPT admin fulfils by hand.\n\nOne open request per country — a second returns `409`.\n","operationId":"createPhoneNumberRequest","x-required-scope":"phone_numbers:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["country"],"properties":{"country":{"type":"string","description":"ISO 3166-1 alpha-2.","example":"IN"},"areaCodePreference":{"type":"string","example":"Mumbai"},"useCase":{"type":"string","enum":["customer_support","appointment_booking","lead_generation","sales","survey_feedback","ads_to_call","other"]},"direction":{"type":"string","enum":["inbound","outbound","both"]},"notes":{"type":"string"},"callWidgetId":{"type":"string"}}}}}},"responses":{"201":{"description":"Request filed"},"409":{"description":"An open request for that country already exists"}}}},"/phone-numbers/requests/{id}":{"get":{"tags":["Phone numbers"],"summary":"Get a phone number request","operationId":"getPhoneNumberRequest","x-required-scope":"phone_numbers:read","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Request"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/organization":{"get":{"tags":["Organization"],"summary":"Get organization configuration","operationId":"getOrganization","x-required-scope":"org_settings:read","responses":{"200":{"description":"Organization configuration"}}},"patch":{"tags":["Organization"],"summary":"Update organization configuration","description":"Accepts only the configuration allowlist: `logo`, `ticketPrefix` and,\nunder `settings`, `timezone`, `allowedDomains`, `maxChatSessions`,\n`retentionDays`, `enableAnalytics`, `aiModels.model`,\n`supportBusinessHours`, `slaPolicy`, `emailToTicket`, `piiRedaction` and\n`channelTicketing`. Any other field is rejected rather than ignored.\n\n`emailToTicket.signingSecret` is not writable here and reads back as a\nboolean, so round-tripping the object cannot blank it.\n","operationId":"updateOrganization","x-required-scope":"org_settings:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated configuration"},"400":{"$ref":"#/components/responses/BadRequest"},"409":{"description":"That ticket prefix belongs to another organization"}}}},"/organization/business-hours":{"get":{"tags":["Organization"],"summary":"Get support business hours","operationId":"getBusinessHours","x-required-scope":"org_settings:read","responses":{"200":{"description":"Business hours"}}},"put":{"tags":["Organization"],"summary":"Replace support business hours","description":"Sending `days` replaces the whole week, so include every open day.","operationId":"setBusinessHours","x-required-scope":"org_settings:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated business hours"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/team":{"get":{"tags":["Team"],"summary":"List organization members","operationId":"listTeamMembers","x-required-scope":"team:read","responses":{"200":{"description":"Member list"}}}},"/team/invites":{"post":{"tags":["Team"],"summary":"Invite a member","description":"Sends a real invitation email. Roles are limited to admin, supervisor and agent.","operationId":"inviteTeamMember","x-required-scope":"team:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","role"],"properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","supervisor","agent"]}}}}}},"responses":{"201":{"description":"Invitation sent"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/team/members/{userId}":{"parameters":[{"in":"path","name":"userId","required":true,"schema":{"type":"string"}}],"patch":{"tags":["Team"],"summary":"Change a member's role","description":"The owner's row cannot be changed and `owner` cannot be assigned —\nownership carries billing and lives on a different field.\n","operationId":"updateTeamMember","x-required-scope":"team:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"properties":{"role":{"type":"string","enum":["admin","supervisor","agent"]}}}}}},"responses":{"200":{"description":"Updated member"},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Team"],"summary":"Remove a member","description":"Removes the membership, not the user account.","operationId":"removeTeamMember","x-required-scope":"team:write","responses":{"204":{"description":"Member removed"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/workflows":{"get":{"tags":["Workflows"],"summary":"List workflows","operationId":"listWorkflows","x-required-scope":"workflows:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"in":"query","name":"status","schema":{"type":"string","enum":["DRAFT","ACTIVE","INACTIVE","ARCHIVED"]}}],"responses":{"200":{"description":"Workflow page"}}},"post":{"tags":["Workflows"],"summary":"Create a workflow","operationId":"createWorkflow","x-required-scope":"workflows:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created workflow"}}}},"/workflows/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Workflows"],"summary":"Get a workflow with its graph","operationId":"getWorkflow","x-required-scope":"workflows:read","responses":{"200":{"description":"Workflow"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Workflows"],"summary":"Update a workflow","operationId":"updateWorkflow","x-required-scope":"workflows:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated workflow"}}}},"/workflows/{id}/publish":{"post":{"tags":["Workflows"],"summary":"Publish a workflow","description":"Snapshots the current graph into the workflow's revision history, bumps\nthe patch version and sets it ACTIVE. Rejected unless the graph has at\nleast one node and both a START and an END node.\n","operationId":"publishWorkflow","x-required-scope":"workflows:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Published workflow"},"400":{"description":"The graph is not publishable"}}}},"/integrations":{"get":{"tags":["Integrations"],"summary":"List channel integrations","operationId":"listIntegrations","x-required-scope":"integrations:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"in":"query","name":"platform","schema":{"type":"string","enum":["WHATSAPP","TELEGRAM","EMAIL","FACEBOOK","INSTAGRAM","SLACK","DISCORD","SMS"]}}],"responses":{"200":{"description":"Integration page"}}},"post":{"tags":["Integrations"],"summary":"Create a channel integration","description":"`config` accepts only the fields valid for the chosen `platform`;\nanything else is rejected. Credentials inside it are write-only.\n","operationId":"createIntegration","x-required-scope":"integrations:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created integration"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/integrations/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Integrations"],"summary":"Get a channel integration","operationId":"getIntegration","x-required-scope":"integrations:read","responses":{"200":{"description":"Integration"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Integrations"],"summary":"Update a channel integration","description":"`platform` cannot change. A blank credential value means \"leave the\nstored value alone\", so a client round-tripping a read cannot wipe one.\n","operationId":"updateIntegration","x-required-scope":"integrations:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated integration"},"400":{"$ref":"#/components/responses/BadRequest"}}},"delete":{"tags":["Integrations"],"summary":"Delete a channel integration","operationId":"deleteIntegration","x-required-scope":"integrations:write","responses":{"204":{"description":"Integration deleted"}}}},"/calendars":{"get":{"tags":["Calendars"],"summary":"List booking calendars","operationId":"listCalendars","x-required-scope":"calendars:read","responses":{"200":{"description":"Calendar page"}}},"post":{"tags":["Calendars"],"summary":"Create a booking calendar","description":"Backed by a data collection, which holds the bookings. A collection can\nback only one calendar, so reusing one returns `409`.\n","operationId":"createCalendar","x-required-scope":"calendars:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created calendar"},"409":{"description":"That data collection already backs another calendar"}}}},"/calendars/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Calendars"],"summary":"Get a booking calendar","operationId":"getCalendar","x-required-scope":"calendars:read","responses":{"200":{"description":"Calendar"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Calendars"],"summary":"Update a booking calendar","operationId":"updateCalendar","x-required-scope":"calendars:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated calendar"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/billing":{"get":{"tags":["Billing"],"summary":"Get billing status","description":"Plan, subscription status, whether a payment method exists, and the\nusage snapshot. There is no write counterpart.\n\n`usageCredits` is the prepaid credit balance: one credit is one AI voice\nminute, and other usage burns fractions, so it can carry two decimals.\n`minutesCredits` is the same balance rounded down, kept for older clients.\n","operationId":"getBilling","x-required-scope":"billing:read","responses":{"200":{"description":"Billing status"}}}},"/setup/checklist":{"get":{"tags":["Setup"],"summary":"Get setup progress","description":"Derived by probing the workspace's resources on every call, not stored —\nso it cannot drift when a resource is deleted. Each item reports whether\nit is complete, whether it is required, and a sentence explaining why.\n","operationId":"getSetupChecklist","x-required-scope":"setup:read","responses":{"200":{"description":"Setup checklist"}}}},"/diagnostics/catalogue":{"get":{"tags":["Diagnostics"],"summary":"List available diagnostic checks and flows","description":"Every check that can be run, grouped by area, plus the symptom-shaped\nflows. A flow owns no probes of its own — it is an ordered list of check\nids that already exist, so it cannot drift from what it names.\n","operationId":"getDiagnosticCatalogue","x-required-scope":"diagnostics:read","responses":{"200":{"description":"Catalogue of checks and flows"}}}},"/diagnostics/run":{"get":{"tags":["Diagnostics"],"summary":"Run diagnostic checks","description":"Runs the selected checks and returns findings with remediation. Naming\nnothing runs the whole catalogue. Unknown ids are rejected rather than\nskipped, so a stale selection fails loudly instead of reporting \"all\nclear\" over checks that never ran.\n\n`status` distinguishes `fail` (a finding about your workspace) from\n`error` (our probe broke and knows nothing). `healthy` is false for\neither, because an unknown is not a pass.\n","operationId":"runDiagnostics","x-required-scope":"diagnostics:read","parameters":[{"in":"query","name":"checks","schema":{"type":"string"},"description":"Comma-separated check ids."},{"in":"query","name":"areas","schema":{"type":"string"},"description":"Comma-separated areas."},{"in":"query","name":"flow","schema":{"type":"string"},"description":"A single flow id."}],"responses":{"200":{"description":"Diagnostic run results"},"400":{"description":"Unknown check","area":null,"or flow id":null}}}},"/contacts":{"get":{"tags":["CRM contacts"],"summary":"List or search contacts","operationId":"listContacts","x-required-scope":"crm:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"in":"query","name":"search","schema":{"type":"string"}}],"responses":{"200":{"description":"Contact page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceList"}}}}}},"post":{"tags":["CRM contacts"],"summary":"Create or upsert a contact","operationId":"upsertContact","x-required-scope":"crm:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created contact"}}}},"/contacts/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["CRM contacts"],"summary":"Get a contact","operationId":"getContact","x-required-scope":"crm:read","responses":{"200":{"description":"Contact"}}},"patch":{"tags":["CRM contacts"],"summary":"Update a contact","operationId":"updateContact","x-required-scope":"crm:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated contact"}}}},"/contacts/{id}/activity":{"get":{"tags":["CRM contacts"],"summary":"Get contact activity","operationId":"getContactActivity","x-required-scope":"crm:read","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Contact activity timeline"}}}},"/tickets":{"get":{"tags":["CRM tickets"],"summary":"List CRM tickets","operationId":"listTickets","x-required-scope":"crm:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"in":"query","name":"status","schema":{"type":"string"}}],"responses":{"200":{"description":"Ticket page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceList"}}}}}},"post":{"tags":["CRM tickets"],"summary":"Create a CRM ticket","operationId":"createTicket","x-required-scope":"crm:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created ticket"}}}},"/tickets/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["CRM tickets"],"summary":"Get a CRM ticket","operationId":"getTicket","x-required-scope":"crm:read","responses":{"200":{"description":"Ticket"}}},"patch":{"tags":["CRM tickets"],"summary":"Update a CRM ticket","operationId":"updateTicket","x-required-scope":"crm:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated ticket"}}}},"/tickets/{id}/accept":{"post":{"tags":["CRM tickets"],"summary":"Accept a ticket","operationId":"acceptTicket","x-required-scope":"crm:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Updated ticket"}}}},"/tickets/{id}/open":{"post":{"tags":["CRM tickets"],"summary":"Move a ticket to open","operationId":"openTicket","x-required-scope":"crm:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Updated ticket"}}}},"/tickets/{id}/hold":{"post":{"tags":["CRM tickets"],"summary":"Put a ticket on hold","operationId":"holdTicket","x-required-scope":"crm:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Updated ticket"}}}},"/tickets/{id}/resume":{"post":{"tags":["CRM tickets"],"summary":"Resume a ticket","operationId":"resumeTicket","x-required-scope":"crm:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Updated ticket"}}}},"/tickets/{id}/resolve":{"post":{"tags":["CRM tickets"],"summary":"Resolve a ticket","operationId":"resolveTicket","x-required-scope":"crm:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated ticket"}}}},"/tickets/{id}/close":{"post":{"tags":["CRM tickets"],"summary":"Close a ticket","operationId":"closeTicket","x-required-scope":"crm:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Updated ticket"}}}},"/tickets/{id}/reassign":{"post":{"tags":["CRM tickets"],"summary":"Reassign a ticket","operationId":"reassignTicket","x-required-scope":"crm:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated ticket"}}}},"/tickets/{id}/archive":{"post":{"tags":["CRM tickets"],"summary":"Archive a ticket","operationId":"archiveTicket","x-required-scope":"crm:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Updated ticket"}}}},"/tickets/{id}/notes":{"get":{"tags":["CRM tickets"],"summary":"List internal ticket notes","operationId":"listTicketNotes","x-required-scope":"crm:read","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Ticket notes"}}},"post":{"tags":["CRM tickets"],"summary":"Add an internal ticket note","operationId":"addTicketNote","x-required-scope":"crm:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created note"}}}},"/conversations":{"get":{"tags":["Conversations"],"summary":"List chat conversations","operationId":"listConversations","x-required-scope":"conversations:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"}],"responses":{"200":{"description":"Conversation page"}}}},"/conversations/{id}":{"get":{"tags":["Conversations"],"summary":"Get a conversation","operationId":"getConversation","x-required-scope":"conversations:read","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Conversation"}}}},"/conversations/{id}/messages":{"get":{"tags":["Conversations"],"summary":"List conversation messages","operationId":"listConversationMessages","x-required-scope":"conversations:read","parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Message page"}}}},"/call-logs":{"get":{"tags":["Call logs"],"summary":"List call logs","operationId":"listCallLogs","x-required-scope":"calls:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"}],"responses":{"200":{"description":"Call log page"}}}},"/call-logs/{id}":{"get":{"tags":["Call logs"],"summary":"Get a call log with transcript and events","operationId":"getCallLog","x-required-scope":"calls:read","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Detailed call log"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/call-logs/{id}/reinitiate":{"post":{"tags":["Call logs"],"summary":"Redial a call that did not reach its target","description":"Only a failed dial, a voicemail, or a call-screening outcome qualifies,\nand the call must have originated from a campaign contact to redial.\nAnything else returns `422` with the reason in `error.code`\n(`not_eligible`, `not_campaign_originated`, `contact_not_found`).\n","operationId":"reinitiateCall","x-required-scope":"calls:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Call queued for redial"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"description":"The call is not eligible for a manual redial.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/call-centre/calls":{"get":{"tags":["Call centre"],"summary":"List rep calls","description":"Human call history with agent, disposition, wrap-up, recording flag and\ntranscription status. Dispositions use the stored spelling\n(`no-answer`), not the underscore form the GraphQL API exposes.\n","operationId":"listCallCentreCalls","x-required-scope":"call_centre:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"},{"in":"query","name":"agentId","description":"Restrict to one rep.","schema":{"type":"string"}},{"in":"query","name":"direction","schema":{"type":"string","enum":["incoming","outgoing"]}},{"in":"query","name":"disposition","schema":{"type":"string","enum":["connected","no-answer","busy","unreachable","voicemail","wrong-number","failed"]}},{"in":"query","name":"leadId","description":"Restrict to calls against one lead row.","schema":{"type":"string"}},{"in":"query","name":"ringGroupId","schema":{"type":"string"}},{"in":"query","name":"needsWrapUp","description":"Only finished calls that still owe a disposition.","schema":{"type":"boolean"}},{"in":"query","name":"search","description":"Matches the phone numbers and the rep's wrap-up notes.","schema":{"type":"string"}},{"in":"query","name":"sortBy","schema":{"type":"string","enum":["startedAt","durationSeconds","disposition"],"default":"startedAt"}},{"in":"query","name":"sortOrder","schema":{"type":"string","enum":["asc","desc"],"default":"desc"}}],"responses":{"200":{"description":"Rep call page"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/call-centre/calls/{id}":{"get":{"tags":["Call centre"],"summary":"Get a rep call with transcript and queue history","description":"Accepts either the call identifier or the carrier call sid. A call\nhandled by the AI voice agent is not addressable here — read it from\n`/call-logs/{id}` instead.\n","operationId":"getCallCentreCall","x-required-scope":"call_centre:read","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Detailed rep call"}}}},"/analytics/overview":{"get":{"tags":["Analytics"],"summary":"Get organization analytics overview","operationId":"getAnalyticsOverview","x-required-scope":"analytics:read","parameters":[{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"}],"responses":{"200":{"description":"Analytics overview"}}}},"/analytics/chat-widgets/{id}":{"get":{"tags":["Analytics"],"summary":"Get conversation and message counts for one chat widget","operationId":"getChatWidgetAnalytics","x-required-scope":"analytics:read","parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"}],"responses":{"200":{"description":"Chat widget analytics"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/analytics/call-widgets/{id}":{"get":{"tags":["Analytics"],"summary":"Get call totals and average duration for one call widget","operationId":"getCallWidgetAnalytics","x-required-scope":"analytics:read","parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"}],"responses":{"200":{"description":"Call widget analytics"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/analytics/crm":{"get":{"tags":["Analytics"],"summary":"Get CRM analytics","operationId":"getCrmAnalytics","x-required-scope":"analytics:read","parameters":[{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"},{"in":"query","name":"includeArchived","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"CRM analytics"}}}},"/meta-lead-ads-integrations":{"get":{"tags":["Meta Lead Ads"],"summary":"List Meta Lead Ads integrations","operationId":"listMetaLeadAdsIntegrations","x-required-scope":"meta_lead_ads:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Integration page"}}},"post":{"tags":["Meta Lead Ads"],"summary":"Create a Meta Lead Ads integration","description":"Credentials are write-only. Send `userAccessToken`, `pageAccessToken`\nand `appSecret` in the body; they are encrypted at rest and read back\nonly as `*Configured` booleans. Supplying `userAccessToken` with a\n`pageId` also derives the page token and checks permissions. A\n`webhookVerifyToken` is generated server-side and cannot be set.\n","operationId":"createMetaLeadAdsIntegration","x-required-scope":"meta_lead_ads:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created integration"},"400":{"$ref":"#/components/responses/BadRequest"},"409":{"description":"An integration for that page already exists in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/meta-lead-ads-integrations/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Meta Lead Ads"],"summary":"Get a Meta Lead Ads integration","operationId":"getMetaLeadAdsIntegration","x-required-scope":"meta_lead_ads:read","responses":{"200":{"description":"Integration"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Meta Lead Ads"],"summary":"Update a Meta Lead Ads integration","operationId":"updateMetaLeadAdsIntegration","x-required-scope":"meta_lead_ads:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated integration"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"An integration for that page already exists in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Meta Lead Ads"],"summary":"Delete a Meta Lead Ads integration","operationId":"deleteMetaLeadAdsIntegration","x-required-scope":"meta_lead_ads:write","responses":{"204":{"description":"Integration deleted"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/meta-lead-ads-integrations/{id}/subscribe":{"post":{"tags":["Meta Lead Ads"],"summary":"Subscribe the page to the leadgen webhook","description":"Requires a configured page access token. On success the integration\nbecomes `ACTIVE`; on a Graph API failure it becomes `ERROR` with the\nreason in `lastError`, and the call still returns `200`.\n","operationId":"subscribeMetaLeadAdsPage","x-required-scope":"meta_lead_ads:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Integration after the subscribe attempt"},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/meta-lead-ads-integrations/{id}/regenerate-verify-token":{"post":{"tags":["Meta Lead Ads"],"summary":"Rotate the webhook verify token","operationId":"regenerateMetaLeadAdsVerifyToken","x-required-scope":"meta_lead_ads:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Integration with the new verify token"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/meta-lead-ads-integrations/{id}/generate-page-access-token":{"post":{"tags":["Meta Lead Ads"],"summary":"Derive a page access token from the stored user token","operationId":"generateMetaLeadAdsPageAccessToken","x-required-scope":"meta_lead_ads:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Integration with the derived page token"},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/meta-lead-ads-integrations/tools/check-token-permissions":{"post":{"tags":["Meta Lead Ads"],"summary":"Check which permissions a Meta token grants","description":"Graph API helper. POST rather than GET so a raw token never lands in a\nquery string or an access log.\n","operationId":"checkMetaTokenPermissions","x-required-scope":"meta_lead_ads:read","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accessToken"],"properties":{"accessToken":{"type":"string"}}}}}},"responses":{"200":{"description":"Permission list"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/meta-lead-ads-integrations/tools/pages":{"post":{"tags":["Meta Lead Ads"],"summary":"List the Facebook pages a token can access","operationId":"listMetaPages","x-required-scope":"meta_lead_ads:read","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accessToken"],"properties":{"accessToken":{"type":"string"}}}}}},"responses":{"200":{"description":"Page list"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/meta-lead-ads-integrations/tools/lead-forms":{"post":{"tags":["Meta Lead Ads"],"summary":"List the lead forms on a page","operationId":"listMetaLeadForms","x-required-scope":"meta_lead_ads:read","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pageId","accessToken"],"properties":{"pageId":{"type":"string"},"accessToken":{"type":"string"}}}}}},"responses":{"200":{"description":"Lead form list"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/outbound-triggers":{"get":{"tags":["Outbound triggers"],"summary":"List outbound triggers","operationId":"listOutboundTriggers","x-required-scope":"outbound_triggers:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"in":"query","name":"callWidgetId","description":"Restrict to triggers on one call widget.","schema":{"type":"string"}}],"responses":{"200":{"description":"Trigger page"},"404":{"$ref":"#/components/responses/NotFound"}}},"post":{"tags":["Outbound triggers"],"summary":"Create an outbound trigger","description":"`callWidgetId`, `sourceType` and `name` are required. A\n`shopify_event` trigger reconciles its Shopify webhooks in the\nbackground after the response is sent.\n","operationId":"createOutboundTrigger","x-required-scope":"outbound_triggers:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created trigger"},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/outbound-triggers/templates":{"get":{"tags":["Outbound triggers"],"summary":"List the trigger template catalogue","operationId":"listOutboundTriggerTemplates","x-required-scope":"outbound_triggers:read","parameters":[{"in":"query","name":"useCase","description":"Restrict the catalogue to one use case.","schema":{"type":"string"}}],"responses":{"200":{"description":"Template list"}}}},"/outbound-triggers/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Outbound triggers"],"summary":"Get an outbound trigger","operationId":"getOutboundTrigger","x-required-scope":"outbound_triggers:read","responses":{"200":{"description":"Trigger"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Outbound triggers"],"summary":"Update an outbound trigger","operationId":"updateOutboundTrigger","x-required-scope":"outbound_triggers:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated trigger"},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Outbound triggers"],"summary":"Delete an outbound trigger","operationId":"deleteOutboundTrigger","x-required-scope":"outbound_triggers:write","responses":{"204":{"description":"Trigger deleted"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/outbound-triggers/{id}/enable":{"post":{"tags":["Outbound triggers"],"summary":"Enable an outbound trigger","operationId":"enableOutboundTrigger","x-required-scope":"outbound_triggers:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Updated trigger"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/outbound-triggers/{id}/disable":{"post":{"tags":["Outbound triggers"],"summary":"Disable an outbound trigger","operationId":"disableOutboundTrigger","x-required-scope":"outbound_triggers:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Updated trigger"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/outbound-triggers/{id}/fire":{"post":{"tags":["Outbound triggers"],"summary":"Fire an outbound trigger for one phone number","description":"Dispatches through the same path as a Shopify or webhook-raised event,\nso the trigger's filters, dedupe window, and rate limit all apply.\n\n`202` means the call was queued. A `200` means the request was valid but\nthe trigger chose not to call — `data.status` is `filtered` or `deduped`\nand `data.reason` says why. Neither is an error, so a client should not\nretry on them.\n","operationId":"fireOutboundTrigger","x-required-scope":"outbound_triggers:write","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to"],"properties":{"to":{"type":"string","description":"E.164 destination number.","example":"+14155550142"},"contactName":{"type":"string","description":"Used by the agent to greet the person by name."},"eventKey":{"type":"string","description":"Deduplication key. Repeat fires with the same key inside the trigger's dedupe window return `deduped`. Omit to skip deduplication.\n"},"metadata":{"type":"object","additionalProperties":true,"description":"Merged into call metadata and exposed to the call script."},"payload":{"type":"object","additionalProperties":true,"description":"Event body used to evaluate the trigger's filters and templates."},"dryRun":{"type":"boolean","description":"Record and evaluate the event without placing a call."}}}}}},"responses":{"200":{"description":"Not called — filtered or deduped"},"202":{"description":"Call queued"},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"description":"Trigger could not be dispatched"}}}},"/data-collections":{"get":{"tags":["Data collections"],"summary":"List data collections","description":"Metadata only — row data is never included, and `entryCount` comes from\nthe collection's own counter.\n","operationId":"listDataCollections","x-required-scope":"data_collections:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Data collection page"}}},"post":{"tags":["Data collections"],"summary":"Create a data collection","operationId":"createDataCollection","x-required-scope":"data_collections:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created data collection"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/data-collections/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Data collections"],"summary":"Get data collection metadata","operationId":"getDataCollection","x-required-scope":"data_collections:read","responses":{"200":{"description":"Data collection"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Data collections"],"summary":"Update a data collection","description":"A collection owned by an integration (`readOnly: true`, e.g. Shopify)\ncannot be edited and returns `400 read_only_collection`.\n","operationId":"updateDataCollection","x-required-scope":"data_collections:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated data collection"},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/data-collections/{id}/leads":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Data collections"],"summary":"List leads in a data collection","description":"The rows of the collection, ordered by row number. Use `updatedSince` to\npull only what has changed since a previous sync.\n","operationId":"listDataCollectionLeads","x-required-scope":"data_collections:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"in":"query","name":"status","description":"Restrict to one pipeline status.","schema":{"type":"string","enum":["new","contacted","callback","won","lost"]}},{"in":"query","name":"updatedSince","description":"Only rows modified at or after this instant.","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Lead page","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ResourceList"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Lead"}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}},"post":{"tags":["Data collections"],"summary":"Upsert leads into a data collection","description":"Up to 1000 leads per request, applied through the same importer the CSV\nupload uses. A row that matches an existing lead updates it rather than\ninserting a second copy; the match column is the collection's\n`primaryKeyColumn` when it declares one, otherwise its phone column.\nPhone matching compares the last ten digits, so `+1 (555) 100-0000` and\n`5551000000` are the same lead.\n\nRows are never rejected as a batch — the response tallies what was\ncreated, updated and skipped, and explains each skipped or malformed\nvalue in `warnings`. Rows that would take the collection past its plan\nrow limit are refused individually and reported the same way; `409` is\nreturned only when the collection was already over its limit and nothing\ncould be written.\n\nA collection owned by an integration (`readOnly: true`, e.g. Shopify)\ncannot be written to and returns `400 read_only_collection`.\n","operationId":"upsertDataCollectionLeads","x-required-scope":"data_collections:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["leads"],"properties":{"leads":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/LeadInput"}}}}}}},"responses":{"200":{"description":"Import tally","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LeadImportResult"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/email-integrations":{"get":{"tags":["Email integrations"],"summary":"List email integrations","description":"The organization's SMTP send / IMAP poll mailboxes. Other social\nplatforms are not exposed here.\n","operationId":"listEmailIntegrations","x-required-scope":"email_integrations:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Email integration page"}}},"post":{"tags":["Email integrations"],"summary":"Create an email integration","description":"`name` is required. SMTP/IMAP passwords are encrypted at rest and read\nback only as `smtpPasswordConfigured` / `imapPasswordConfigured`. On a\nPATCH, a blank password means \"unchanged\" rather than \"clear\", so a\nclient can round-trip the redacted object safely.\n","operationId":"createEmailIntegration","x-required-scope":"email_integrations:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"201":{"description":"Created email integration"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/email-integrations/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"get":{"tags":["Email integrations"],"summary":"Get an email integration","operationId":"getEmailIntegration","x-required-scope":"email_integrations:read","responses":{"200":{"description":"Email integration"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Email integrations"],"summary":"Update an email integration","operationId":"updateEmailIntegration","x-required-scope":"email_integrations:write","requestBody":{"$ref":"#/components/requestBodies/ResourceInput"},"responses":{"200":{"description":"Updated email integration"},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Email integrations"],"summary":"Delete an email integration","operationId":"deleteEmailIntegration","x-required-scope":"email_integrations:write","responses":{"204":{"description":"Email integration deleted"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/hooks/events":{"get":{"tags":["Event subscriptions"],"summary":"List subscribable events","operationId":"listWebhookEvents","x-required-scope":"webhooks:read","responses":{"200":{"description":"Supported event names"}}}},"/hooks/samples":{"get":{"tags":["Event subscriptions"],"summary":"Recent occurrences of an event","description":"Returns the most recent real occurrences of an event in the same\n`{ event, occurredAt, organizationId, data }` envelope a subscriber\nreceives, so an integration can show sample data without waiting for a\nlive delivery. `occurredAt` is when the event actually happened.\n\nEach payload is built by the same code that builds the delivered\nwebhook, so a record read here is structurally identical to one\ndelivered later.\n\nEvents with no recorded history, and subscribable events that cannot\nyet be reconstructed, return an empty list rather than an error.\n","operationId":"listWebhookSamples","x-required-scope":"webhooks:read","parameters":[{"name":"event","in":"query","required":true,"schema":{"type":"string"},"description":"One of the names from `GET /hooks/events`."},{"name":"limit","in":"query","schema":{"type":"integer","default":3,"minimum":1,"maximum":10}}],"responses":{"200":{"description":"Recent event envelopes"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/hooks":{"get":{"tags":["Event subscriptions"],"summary":"List event subscriptions","operationId":"listWebhookSubscriptions","x-required-scope":"webhooks:read","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/Limit"},{"name":"event","in":"query","schema":{"type":"string"},"description":"Filter to a single event name."}],"responses":{"200":{"description":"Subscription page"},"400":{"$ref":"#/components/responses/BadRequest"}}},"post":{"tags":["Event subscriptions"],"summary":"Subscribe to an event","description":"Registers a URL to receive an event. Deliveries are signed with the\nreturned `signingSecret` using the `X-FloGPT-Signature:\nt=<unix>,v1=<hex>` header, where the signed string is\n`<timestamp>.<raw body>`.\n\nIdempotent on `(event, targetUrl)`: re-subscribing reactivates the\nexisting subscription and issues a fresh secret rather than creating a\nduplicate, so one event is never delivered twice to the same URL.\n\n`signingSecret` is returned by this endpoint only and cannot be read\nback afterwards. `targetUrl` must be HTTPS and publicly routable.\n","operationId":"createWebhookSubscription","x-required-scope":"webhooks:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["event","targetUrl"],"properties":{"event":{"type":"string","description":"One of the names from `GET /hooks/events`.","example":"missed_call"},"targetUrl":{"type":"string","format":"uri","example":"https://hooks.zapier.com/hooks/standard/1/abc"}}}}}},"responses":{"201":{"description":"Subscription created","including the signing secret":null},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/hooks/{id}":{"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"delete":{"tags":["Event subscriptions"],"summary":"Unsubscribe","operationId":"deleteWebhookSubscription","x-required-scope":"webhooks:write","responses":{"204":{"description":"Subscription deleted"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}