# Flownally API API Reference

Version: 1.0.0

# Send a message

**POST** `/messages`

Starts or continues a customer conversation by recipient. Choose an inbox first with
`GET /inboxes`, optionally prepare WhatsApp template or media content, then send with a
recipient that points to an existing conversation, a known contact, or a channel recipient.
The response uses `202 Accepted`; poll `GET /messages/{id}` or the conversation messages
endpoint to follow delivery state without webhooks.

Authentication: bearerAuth
Required scopes: `message:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Send an outbound message in an authorized conversation context.

## Request body

- `application/json`: `CreateMessageRequest` required.

## Responses

- `202`: Message accepted for delivery
- `default`: Error

# Get message

**GET** `/messages/{id}`

Returns delivery state and conversation context for polling integrations.

Authentication: bearerAuth
Required scopes: `conversation:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read a message through its conversation context.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Message
- `default`: Error

# Upload message media

**POST** `/media/uploads`

Uploads a file and returns a `mediaId` handle for later message sends.
Pass the handle as `content.media.mediaId` when sending media content.
Use the same Idempotency-Key when retrying the same upload request.

Authentication: bearerAuth
Required scopes: `message:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Upload media for a future message send.

## Request body

- `multipart/form-data`: `CreateMediaUploadRequest` required.

## Responses

- `201`: Media uploaded
- `default`: Error

# List conversations

**GET** `/conversations`

Returns conversations visible to the authenticated user. Use this before polling
message history or assigning work to a teammate.

Authentication: bearerAuth
Required scopes: `conversation:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List conversations in the caller's conversation scope.

## Parameters

- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)
- `interacted` (query boolean, optional)
- `status` (query array<enum>, optional)
- `sort` (query enum, optional)

## Responses

- `200`: List of conversations
- `default`: Error

# Get conversation

**GET** `/conversations/{id}`

Returns the current conversation context, including the sendability window that tells
whether an agent or bot can reply right now.

Authentication: bearerAuth
Required scopes: `conversation:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read a conversation in the caller's conversation scope.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Conversation details
- `default`: Error

# List conversation messages

**GET** `/conversations/{id}/messages`

Returns the message history for a conversation, newest-page token included when more messages are available.

Authentication: bearerAuth
Required scopes: `conversation:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List messages for a conversation in scope.

## Parameters

- `id` (path string, required)
- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)

## Responses

- `200`: List of messages
- `default`: Error

# Send message in a conversation

**POST** `/conversations/{id}/messages`

Sends a reply in an existing conversation. Pass `inboxId` when you want to choose the
sender explicitly; otherwise Flownally uses the latest open session for that conversation.
The response is returned as soon as the message is accepted, so poll the message resource
to follow delivery state when you are not using webhooks.

Authentication: bearerAuth
Required scopes: `message:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Send a message in a conversation in scope.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `SendMessageRequest` required.

## Responses

- `202`: Message accepted for delivery
- `default`: Error

# Add or remove a reaction on a message

**POST** `/messages/{id}/reactions`

Authentication: bearerAuth
Required scopes: `message:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Add or remove a reaction in an authorized conversation context.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `SendReactionRequest` required.

## Responses

- `200`: Reaction sent
- `default`: Error

# Reassign conversation

**POST** `/conversations/{id}/reassign`

Reassigns a conversation to a different user

Authentication: bearerAuth
Required scopes: `conversation:assign`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Reassign a conversation in scope.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `ReassignConversationRequest` required.

## Responses

- `204`: Conversation reassigned
- `default`: Error

# Mark conversation as read

**POST** `/conversations/{id}/read`

Marks messages up to the specified message as read for the authenticated user

Authentication: bearerAuth
Required scopes: `conversation:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Mark a conversation in scope as read.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `MarkConversationReadRequest` required.

## Responses

- `200`: Conversation marked as read
- `default`: Error

# Close conversation

**POST** `/conversations/{id}/close`

Closes the active session for the conversation

Authentication: bearerAuth
Required scopes: `conversation:close`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Close a conversation in scope.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Conversation closed
- `default`: Error

# Get inboxes

**GET** `/inboxes`

Returns the connected channels that can send or receive customer messages in this
workspace. Use an inbox ID as the sender when starting outbound messages.

Authentication: bearerAuth
Required scopes: `inbox:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List inboxes in the workspace.

## Responses

- `200`: List of inboxes
- `default`: Error

# Create inbox

**POST** `/inboxes`

Connects one or more channel resources and makes them available as Flownally inboxes.

Authentication: bearerAuth
Required scopes: `inbox:create`
Allowed roles: `owner`, `admin`
Authorization: Create an inbox in the workspace.

## Request body

- `application/json`: `CreateInboxRequest` required.

## Responses

- `200`: Inbox created successfully
- `default`: Error

# Delete inbox

**DELETE** `/inboxes/{id}`

Removes an inbox from the workspace so it can no longer receive or send messages.

Authentication: bearerAuth
Required scopes: `inbox:delete`
Allowed roles: `owner`, `admin`
Authorization: Delete an inbox in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Inbox deleted successfully
- `default`: Error

# Assign team to inbox

**PUT** `/inboxes/{id}/team`

Routes new conversations from this inbox to the selected team.

Authentication: bearerAuth
Required scopes: `inbox:update`
Allowed roles: `owner`, `admin`
Authorization: Assign a team to an inbox.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `SetInboxTeamRequest` required.

## Responses

- `200`: Team set
- `default`: Error

# Clear team from inbox

**DELETE** `/inboxes/{id}/team`

Removes team routing so conversations use the workspace fallback.

Authentication: bearerAuth
Required scopes: `inbox:update`
Allowed roles: `owner`, `admin`
Authorization: Clear team assignment from an inbox.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Team cleared
- `default`: Error

# List WhatsApp templates

**GET** `/inboxes/{id}/whatsapp/templates`

Returns approved and pending WhatsApp templates available for this inbox. Use template
names and languages from this response when sending `template` message content.

Authentication: bearerAuth
Required scopes: `inbox:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List WhatsApp templates for an inbox.

## Parameters

- `id` (path string, required)

## Responses

- `200`: List of WhatsApp templates
- `default`: Error

# List WABA phone numbers

**GET** `/whatsapp/{wabaId}/phone-numbers`

Returns WhatsApp phone numbers that can be connected as Flownally inboxes.

Authentication: bearerAuth
Required scopes: `inbox:create`
Allowed roles: `owner`, `admin`
Authorization: List WhatsApp phone numbers available for inbox creation.

## Parameters

- `wabaId` (path string, required)

## Responses

- `200`: List of phone numbers
- `default`: Error

# Get contacts

**GET** `/contacts`

Returns customer profiles with identities, tags, and custom fields. Use filters to
find the segment you want to message, enroll in journeys, or keep in sync.

Authentication: bearerAuth
Required scopes: `contact:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List contacts in the caller's contact scope.

## Parameters

- `pageToken` (query string, optional) - Opaque token from the previous response's `nextPageToken`.
- `pageSize` (query integer, optional)
- `search` (query string, optional) - Case-insensitive substring search across name, cf_email, cf_phone, identity phone, and tag name.
- `customFields` (query object, optional) - Filter by custom field values (AND logic)
- `tagIds` (query array<string>, optional) - Include contacts tagged with any of the listed tag IDs (OR semantics).
- `channels` (query array<enum>, optional) - Include contacts that have an identity on any of the listed channels (OR semantics).
- `archived` (query boolean, optional) - Tri-state filter. Omit to exclude archived contacts (default),
set to true to return only archived contacts, or false to return only non-archived.
- `createdAfter` (query string, optional) - Inclusive lower bound on createdAt (RFC3339).
- `createdBefore` (query string, optional) - Exclusive upper bound on createdAt (RFC3339).
- `orderBy` (query enum, optional)
- `orderDirection` (query enum, optional)

## Responses

- `200`: List of contacts
- `default`: Error

# Create contact

**POST** `/contacts`

Creates a customer profile that can be matched to messages and events.

Authentication: bearerAuth
Required scopes: `contact:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Create a contact in the workspace.

## Request body

- `application/json`: `CreateContactRequest` required.

## Responses

- `201`: Contact created
- `default`: Error

# Count contacts

**GET** `/contacts/count`

Returns the tenant's contact count for a navigation/dashboard badge. This is independent
of any list filters; the archived query parameter selects which bucket to count.

Authentication: bearerAuth
Required scopes: `contact:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Count contacts in the caller's contact scope.

## Parameters

- `archived` (query boolean, optional) - Omit or pass false for non-archived count; pass true for archived-bucket count.

## Responses

- `200`: Contact count
- `default`: Error

# Get contact by ID

**GET** `/contacts/{id}`

Returns one customer profile with identities, tags, custom fields, and archive state.

Authentication: bearerAuth
Required scopes: `contact:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read a contact in the caller's contact scope.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Contact found
- `404`: Contact not found
- `default`: Error

# Update contact

**PATCH** `/contacts/{id}`

Updates mutable contact fields. Fields omitted from the body are left unchanged.

Authentication: bearerAuth
Required scopes: `contact:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Update a contact in the caller's contact scope.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `UpdateContactRequest` required.

## Responses

- `200`: Contact updated
- `404`: Contact not found
- `default`: Error

# Delete contact

**DELETE** `/contacts/{id}`

Soft deletes a contact. Use archive when you only want to hide the contact from active lists.

Authentication: bearerAuth
Required scopes: `contact:delete`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Delete a contact in the caller's contact scope.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Contact deleted
- `404`: Contact not found
- `default`: Error

# Import contacts from CSV

**POST** `/contacts/import`

Uploads a CSV file and starts a background contact import. Poll the returned import ID
to review completion counts and row-level errors.

Authentication: bearerAuth
Required scopes: `contact:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Import contacts into the workspace.

## Request body

- `multipart/form-data` required.

## Responses

- `202`: Import initiated
- `default`: Error

# Get import status

**GET** `/contacts/imports/{id}`

Returns progress and row-level failures for a contact import.

Authentication: bearerAuth
Required scopes: `contact:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read contact import status.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Import status
- `default`: Error

# Archive contact

**POST** `/contacts/{id}/archive`

Hides a contact from active lists without deleting its history.

Authentication: bearerAuth
Required scopes: `contact:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Archive a contact in the caller's contact scope.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Contact archived
- `404`: Contact not found
- `409`: Contact is already archived
- `default`: Error

# Unarchive contact

**POST** `/contacts/{id}/unarchive`

Restores an archived contact to active lists.

Authentication: bearerAuth
Required scopes: `contact:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Unarchive a contact in the caller's contact scope.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Contact unarchived
- `404`: Contact not found
- `409`: Contact is not archived
- `default`: Error

# Set contact field value

**PUT** `/contacts/{contactId}/custom-fields/{customFieldId}`

Sets or clears one custom field value on a contact. Send an empty string to clear
the value while keeping the field definition available for future use.

Authentication: bearerAuth
Required scopes: `contact:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Set a custom field value on a contact in scope.

## Parameters

- `contactId` (path string, required)
- `customFieldId` (path string, required)

## Request body

- `application/json`: `SetContactCustomFieldValueRequest` required.

## Responses

- `204`: Contact field value set
- `default`: Error

# Add tag to contact

**POST** `/contacts/{contactId}/tags/{tagId}`

Adds a label to the contact for segmentation, routing, or reporting.

Authentication: bearerAuth
Required scopes: `contact:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Tag a contact in scope.

## Parameters

- `contactId` (path string, required)
- `tagId` (path string, required)

## Responses

- `204`: Tag added
- `default`: Error

# Remove tag from contact

**DELETE** `/contacts/{contactId}/tags/{tagId}`

Removes a label from the contact.

Authentication: bearerAuth
Required scopes: `contact:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Remove a tag from a contact in scope.

## Parameters

- `contactId` (path string, required)
- `tagId` (path string, required)

## Responses

- `204`: Tag removed
- `default`: Error

# List tags

**GET** `/tags`

Returns the labels available for organizing contacts.

Authentication: bearerAuth
Required scopes: `tag:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List contact tags in the workspace.

## Responses

- `200`: List of tags
- `default`: Error

# Create tag

**POST** `/tags`

Creates a label that can be attached to contacts.

Authentication: bearerAuth
Required scopes: `tag:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Create a contact tag in the workspace.

## Request body

- `application/json`: `CreateTagRequest` required.

## Responses

- `201`: Tag created
- `default`: Error

# Delete tag

**DELETE** `/tags/{id}`

Authentication: bearerAuth
Required scopes: `tag:delete`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Delete a contact tag in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Tag deleted
- `default`: Error

# List contact field definitions

**GET** `/contacts/custom-fields`

Returns the reusable custom fields that can store structured customer context on
contacts, such as lifecycle stage, preferred store, or renewal date.

Authentication: bearerAuth
Required scopes: `custom_field:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List contact field definitions.

## Responses

- `200`: Contact field definitions
- `default`: Error

# Create contact field definition

**POST** `/contacts/custom-fields`

Creates a reusable custom field for storing customer context on contacts.

Authentication: bearerAuth
Required scopes: `custom_field:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Create a contact field definition.

## Request body

- `application/json`: `CreateCustomFieldDefinitionRequest` required.

## Responses

- `201`: Contact field definition created
- `default`: Error

# Update contact field definition

**PATCH** `/contacts/custom-fields/{id}`

Renames a contact field definition or changes its value type.

Authentication: bearerAuth
Required scopes: `custom_field:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Update a contact field definition.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `UpdateCustomFieldDefinitionRequest` required.

## Responses

- `200`: Contact field definition updated
- `default`: Error

# Delete contact field definition

**DELETE** `/contacts/custom-fields/{id}`

Deletes a contact field definition that is no longer needed.

Authentication: bearerAuth
Required scopes: `custom_field:delete`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Delete a contact field definition.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Contact field definition deleted
- `default`: Error

# List property field definitions

**GET** `/properties/custom-fields`

Returns the reusable custom fields that can store structured context on property
records, such as region, tier, or renewal date.

Authentication: bearerAuth
Required scopes: `custom_field:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List property field definitions.

## Responses

- `200`: Property field definitions
- `default`: Error

# Create property field definition

**POST** `/properties/custom-fields`

Creates a reusable custom field for storing context on property records.

Authentication: bearerAuth
Required scopes: `custom_field:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Create a property field definition.

## Request body

- `application/json`: `CreateCustomFieldDefinitionRequest` required.

## Responses

- `201`: Property field definition created
- `default`: Error

# Update property field definition

**PATCH** `/properties/custom-fields/{id}`

Renames a property field definition or changes its value type.

Authentication: bearerAuth
Required scopes: `custom_field:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Update a property field definition.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `UpdateCustomFieldDefinitionRequest` required.

## Responses

- `200`: Property field definition updated
- `default`: Error

# Delete property field definition

**DELETE** `/properties/custom-fields/{id}`

Deletes a property field definition that is no longer needed.

Authentication: bearerAuth
Required scopes: `custom_field:delete`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Delete a property field definition.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Property field definition deleted
- `default`: Error

# List journeys

**GET** `/journeys`

Returns customer journeys that can react to contact events and conversation moments.

Authentication: bearerAuth
Required scopes: `journey:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List journeys in the workspace.

## Parameters

- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)

## Responses

- `200`: List of journeys
- `default`: Error

# Create journey

**POST** `/journeys`

Creates a journey draft. Publish and activate it before contacts can enter.

Authentication: bearerAuth
Required scopes: `journey:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Create a journey in the workspace.

## Request body

- `application/json`: `CreateJourneyRequest` required.

## Responses

- `201`: Journey created
- `default`: Error

# Get journey

**GET** `/journeys/{id}`

Returns a journey draft, its status, and the active version when one is running.

Authentication: bearerAuth
Required scopes: `journey:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read a journey in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Journey details
- `default`: Error

# Update journey

**PATCH** `/journeys/{id}`

Updates a journey draft. Publish again to snapshot changes for activation.

Authentication: bearerAuth
Required scopes: `journey:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Update a journey in the workspace.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `UpdateJourneyRequest` required.

## Responses

- `200`: Journey updated
- `default`: Error

# Delete journey

**DELETE** `/journeys/{id}`

Deletes a journey. Active journeys must be paused first.

Authentication: bearerAuth
Required scopes: `journey:delete`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Delete a journey in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Journey deleted
- `default`: Error

# Publish journey

**POST** `/journeys/{id}/publish`

Validates and snapshots the current draft definition as an immutable version.

Authentication: bearerAuth
Required scopes: `journey:publish`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Publish a journey version.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Version published
- `default`: Error

# Activate journey

**POST** `/journeys/{id}/activate`

Activates a journey to start processing events. Requires published version.

Authentication: bearerAuth
Required scopes: `journey:activate`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Activate a journey.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Journey activated
- `default`: Error

# Pause journey

**POST** `/journeys/{id}/pause`

Pauses an active journey. Running instances continue to completion.

Authentication: bearerAuth
Required scopes: `journey:activate`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Pause a journey.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Journey paused
- `default`: Error

# List journey versions

**GET** `/journeys/{id}/versions`

Authentication: bearerAuth
Required scopes: `journey:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List journey versions.

## Parameters

- `id` (path string, required)
- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)

## Responses

- `200`: List of versions
- `default`: Error

# Get journey version

**GET** `/journeys/versions/{versionId}`

Authentication: bearerAuth
Required scopes: `journey:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read a journey version.

## Parameters

- `versionId` (path string, required)

## Responses

- `200`: Version details
- `default`: Error

# List chatbots

**GET** `/chatbots`

Returns chatbot drafts and active automations for the workspace.

Authentication: bearerAuth
Required scopes: `journey:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List chatbots in the workspace.

## Parameters

- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)

## Responses

- `200`: List of chatbots
- `default`: Error

# Create chatbot

**POST** `/chatbots`

Creates a chatbot draft that can later be published and activated.

Authentication: bearerAuth
Required scopes: `journey:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Create a chatbot in the workspace.

## Request body

- `application/json`: `CreateChatbotRequest` required.

## Responses

- `201`: Chatbot created
- `default`: Error

# Get chatbot

**GET** `/chatbots/{id}`

Returns a chatbot draft, its status, and the active version when one is running.

Authentication: bearerAuth
Required scopes: `journey:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read a chatbot in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Chatbot details
- `default`: Error

# Update chatbot

**PATCH** `/chatbots/{id}`

Updates chatbot draft details or team assignments. Publish again to snapshot changes.

Authentication: bearerAuth
Required scopes: `journey:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Update a chatbot in the workspace.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `UpdateChatbotRequest` required.

## Responses

- `200`: Chatbot updated
- `default`: Error

# Delete chatbot

**DELETE** `/chatbots/{id}`

Deletes a chatbot. Active chatbots must be paused first.

Authentication: bearerAuth
Required scopes: `journey:delete`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Delete a chatbot in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Chatbot deleted
- `default`: Error

# Publish chatbot

**POST** `/chatbots/{id}/publish`

Validates and snapshots the current draft definition as an immutable version.

Authentication: bearerAuth
Required scopes: `journey:publish`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Publish a chatbot version.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Version published
- `default`: Error

# Activate chatbot

**POST** `/chatbots/{id}/activate`

Activates a chatbot. Requires a published version.

Authentication: bearerAuth
Required scopes: `journey:activate`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Activate a chatbot.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Chatbot activated
- `default`: Error

# Pause chatbot

**POST** `/chatbots/{id}/pause`

Pauses an active chatbot.

Authentication: bearerAuth
Required scopes: `journey:activate`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Pause a chatbot.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Chatbot paused
- `default`: Error

# List chatbot versions

**GET** `/chatbots/{id}/versions`

Authentication: bearerAuth
Required scopes: `journey:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List chatbot versions.

## Parameters

- `id` (path string, required)
- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)

## Responses

- `200`: List of versions
- `default`: Error

# Get chatbot version

**GET** `/chatbots/{id}/versions/{versionId}`

Authentication: bearerAuth
Required scopes: `journey:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read a chatbot version.

## Parameters

- `id` (path string, required)
- `versionId` (path string, required)

## Responses

- `200`: Version details
- `default`: Error

# List event types

**GET** `/event-types`

Returns predefined and custom event types that can be recorded against contacts.

Authentication: bearerAuth
Required scopes: `event_type:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List event types in the workspace.

## Parameters

- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)

## Responses

- `200`: List of event types
- `default`: Error

# Create event type

**POST** `/event-types`

Creates a custom event type with an optional JSON Schema for payload validation.

Authentication: bearerAuth
Required scopes: `event_type:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Create an event type in the workspace.

## Request body

- `application/json`: `CreateEventTypeRequest` required.

## Responses

- `201`: Event type created
- `default`: Error

# Get event type

**GET** `/event-types/{id}`

Authentication: bearerAuth
Required scopes: `event_type:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read an event type in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Event type details
- `default`: Error

# Update event type

**PATCH** `/event-types/{id}`

Updates an event type. Schema changes must be backward-compatible with stored events.

Authentication: bearerAuth
Required scopes: `event_type:update`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Update an event type in the workspace.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `UpdateEventTypeRequest` required.

## Responses

- `200`: Event type updated
- `default`: Error

# Delete event type

**DELETE** `/event-types/{id}`

Deletes a custom event type when it is not predefined and has no recorded events.

Authentication: bearerAuth
Required scopes: `event_type:delete`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Delete an event type in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Event type deleted
- `default`: Error

# List events

**GET** `/events`

Returns recorded customer events, optionally filtered by event type.

Authentication: bearerAuth
Required scopes: `event:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List events in the workspace.

## Parameters

- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)
- `eventTypeId` (query string, optional)

## Responses

- `200`: List of events
- `default`: Error

# Create event

**POST** `/events`

Records an event against a contact. Prefer `POST /contacts/{contactId}/events` for new integrations.

Authentication: bearerAuth
Required scopes: `event:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Create an event in the workspace.

## Request body

- `application/json`: `CreateEventRequest` required.

## Responses

- `201`: Event created
- `409`: Event with this idempotency key already exists
- `default`: Error

# Get event

**GET** `/events/{id}`

Returns one recorded customer event.

Authentication: bearerAuth
Required scopes: `event:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read an event in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Event details
- `default`: Error

# List events by contact

**GET** `/contacts/{contactId}/events`

Returns the timeline of events recorded for a contact.

Authentication: bearerAuth
Required scopes: `event:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List events for a contact.

## Parameters

- `contactId` (path string, required)
- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)
- `eventTypeId` (query string, optional)

## Responses

- `200`: List of events
- `default`: Error

# Create event for contact

**POST** `/contacts/{contactId}/events`

Records a customer moment such as a purchase, signup, or plan change. Matching active
journeys can react to the event after it is accepted.

Authentication: bearerAuth
Required scopes: `event:create`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Create an event for a contact.

## Parameters

- `contactId` (path string, required)

## Request body

- `application/json`: `CreateContactEventRequest` required.

## Responses

- `201`: Event created
- `409`: Event with this idempotency key already exists
- `default`: Error

# List teams

**GET** `/teams`

Returns teams that can own inboxes, conversations, and chatbot assignments.

Authentication: bearerAuth
Required scopes: `team:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List teams in the workspace.

## Parameters

- `pageSize` (query integer, optional)
- `pageToken` (query string, optional)

## Responses

- `200`: List of teams
- `default`: Error

# Create team

**POST** `/teams`

Creates a team for routing conversations and organizing teammates.

Authentication: bearerAuth
Required scopes: `team:create`
Allowed roles: `owner`, `admin`
Authorization: Create a team in the workspace.

## Request body

- `application/json`: `CreateTeamRequest` required.

## Responses

- `201`: Team created
- `default`: Error

# Get team

**GET** `/teams/{id}`

Returns one team and its current agent assignments.

Authentication: bearerAuth
Required scopes: `team:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read a team in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `200`: Team details
- `default`: Error

# Update team

**PUT** `/teams/{id}`

Updates a team's display name or description.

Authentication: bearerAuth
Required scopes: `team:update`
Allowed roles: `owner`, `admin`
Authorization: Update a team in the workspace.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `UpdateTeamRequest` required.

## Responses

- `200`: Team updated
- `default`: Error

# Delete team

**DELETE** `/teams/{id}`

Deletes a team that is no longer used for routing or assignments.

Authentication: bearerAuth
Required scopes: `team:delete`
Allowed roles: `owner`, `admin`
Authorization: Delete a team in the workspace.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Team deleted
- `default`: Error

# List team agents

**GET** `/teams/{id}/agents`

Returns users assigned to the team.

Authentication: bearerAuth
Required scopes: `team:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List agents assigned to a team.

## Parameters

- `id` (path string, required)

## Responses

- `200`: List of agents
- `default`: Error

# Add agent to team

**POST** `/teams/{id}/agents`

Assigns a user to the team so they can work conversations routed there.

Authentication: bearerAuth
Required scopes: `team:assign_user`
Allowed roles: `owner`, `admin`
Authorization: Add an agent to a team.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `AddAgentToTeamRequest` required.

## Responses

- `201`: Agent added
- `default`: Error

# Remove agent from team

**DELETE** `/teams/{id}/agents/{userId}`

Removes a user from the team.

Authentication: bearerAuth
Required scopes: `team:remove_user`
Allowed roles: `owner`, `admin`
Authorization: Remove an agent from a team.

## Parameters

- `id` (path string, required)
- `userId` (path string, required)

## Responses

- `204`: Agent removed
- `default`: Error

# Get users

**GET** `/users`

Returns people who can work in the current Flownally workspace.

Authentication: bearerAuth
Required scopes: `user:list`
Allowed roles: `owner`, `admin`, `agent`
Authorization: List workspace users.

## Responses

- `200`: List of users
- `default`: Error

# Get current user profile

**GET** `/users/me`

Returns the authenticated user's profile.

Authentication: bearerAuth
Required scopes: `user:read_self`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read the caller's user record.

## Responses

- `200`: Current user profile
- `default`: Error

# Update current user profile

**PATCH** `/users/me`

Updates the authenticated user's name or avatar URL.

Authentication: bearerAuth
Required scopes: `user:update_self`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Update the caller's user profile.

## Request body

- `application/json`: `UpdateProfileRequest` required.

## Responses

- `200`: Updated user profile
- `default`: Error

# Get current user's global unread message count

**GET** `/users/me/unread-count`

Returns the total number of unread messages across all sessions for the
authenticated user. This is the authoritative value used to hydrate the
inbox badge on page load and on WebSocket reconnect; WebSocket deltas
update the badge between requests.

Authentication: bearerAuth
Required scopes: `user:read_self`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read the caller's unread conversation count.

## Responses

- `200`: Global unread count
- `default`: Error

# Authoritative unread summary with monotonic version

**GET** `/users/me/unread-summary`

Returns the unread messages count for the authenticated user plus a
monotonically-increasing `version`. Clients use `version` to discard
stale WebSocket `unread_count_update` / `conversation_read` events.
Called by the web client on WebSocket (re)connect to bootstrap the
unread badge.

Authentication: bearerAuth
Required scopes: `user:read_self`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read the caller's unread conversation summary.

## Responses

- `200`: Unread summary
- `default`: Error

# Get user

**GET** `/users/{userId}`

Returns one workspace member, including role and team membership.

Authentication: bearerAuth
Required scopes: `user:read`
Allowed roles: `owner`, `admin`, `agent`
Authorization: Read a workspace user.

## Parameters

- `userId` (path string, required)

## Responses

- `200`: User
- `404`: User not found
- `default`: Error

# Update a member's role

**PUT** `/users/{userId}/role`

Changes the role of a tenant member. The caller must hold a role with
authority over the requested role transition. Admins can shuffle
members between admin and agent; only Owners can mint or demote
Owners. A tenant must retain at least one Owner — demoting the last
one is rejected.

Authentication: bearerAuth
Required scopes: `user:assign_role_admin`, `user:assign_role_agent`, `user:assign_role_owner`
Allowed roles: `owner`, `admin`
Authorization: Assign a user role; required action depends on request body role.

## Parameters

- `userId` (path string, required)

## Request body

- `application/json`: `UpdateUserRoleRequest` required.

## Responses

- `200`: Member updated
- `400`: Invalid request. Codes:
  - `auth_user_invalid_role` — role is not one of owner/admin/agent
  - `auth_user_self_role_change_forbidden` — caller targeted themselves
- `403`: Caller lacks authority for this role change. Codes:
  - `auth_authz_user_assign_role_denied` — matrix rejects the new role (e.g. Admin attempting Owner)
  - `auth_user_role_assignment_forbidden` — caller cannot edit an Owner target
- `404`: Target user not found in the caller's tenant. Code:
  - `auth_user_not_found`
- `409`: Tenant invariant would be violated. Code:
  - `auth_user_last_owner_required` — demotion would leave zero Owners
- `default`: Error

# Get billing status

**GET** `/billing/status`

Returns subscription state for the workspace, including access and payment issue flags.

Authentication: bearerAuth
Required scopes: `billing:read`
Allowed roles: `owner`, `admin`
Authorization: Read billing status for the workspace.

## Responses

- `200`: Billing status
- `default`: Error

# List service accounts

**GET** `/service-accounts`

Returns service accounts that can own API keys for server-to-server integrations.

Authentication: bearerAuth
Required scopes: `api_key:list`
Allowed roles: `owner`, `admin`
Authorization: List service accounts.

## Responses

- `200`: List of service accounts
- `default`: Error

# Create service account

**POST** `/service-accounts`

Creates a named integration identity before issuing API keys.

Authentication: bearerAuth
Required scopes: `api_key:create`
Allowed roles: `owner`, `admin`
Authorization: Create a service account.

## Request body

- `application/json`: `CreateServiceAccountRequest` required.

## Responses

- `200`: Service account created
- `default`: Error

# Delete service account

**DELETE** `/service-accounts/{id}`

Deletes a service account and prevents its keys from being used.

Authentication: bearerAuth
Required scopes: `api_key:revoke`
Allowed roles: `owner`, `admin`
Authorization: Delete a service account.

## Parameters

- `id` (path string, required)

## Responses

- `204`: Service account deleted
- `default`: Error

# List API keys for a service account

**GET** `/service-accounts/{id}/api-keys`

Returns API keys created for the service account. Plaintext key values are never returned here.

Authentication: bearerAuth
Required scopes: `api_key:list`
Allowed roles: `owner`, `admin`
Authorization: List API keys.

## Parameters

- `id` (path string, required)

## Responses

- `200`: List of API keys
- `default`: Error

# Create API key

**POST** `/service-accounts/{id}/api-keys`

Creates an API key for a service account. Store the plaintext key from this response because it is shown once.

Authentication: bearerAuth
Required scopes: `api_key:create`
Allowed roles: `owner`, `admin`
Authorization: Create an API key.

## Parameters

- `id` (path string, required)

## Request body

- `application/json`: `CreateAPIKeyRequest` required.

## Responses

- `200`: API key created (plaintext key shown once)
- `default`: Error

# Revoke API key

**DELETE** `/api-keys/{id}`

Revokes an API key so it can no longer authenticate requests.

Authentication: bearerAuth
Required scopes: `api_key:revoke`
Allowed roles: `owner`, `admin`
Authorization: Revoke an API key.

## Parameters

- `id` (path string, required)

## Responses

- `204`: API key revoked
- `default`: Error

# Get webhook settings

**GET** `/webhooks`

Returns the current tenant webhook configuration, selected event types, and event types that can be subscribed to.

Authentication: bearerAuth
Required scopes: `webhook:read`
Allowed roles: `owner`, `admin`
Authorization: Read webhook settings.

## Responses

- `200`: Webhook settings
- `default`: Error

# Update webhook settings

**PATCH** `/webhooks`

Partially updates the single webhook configuration for the current tenant. Select event types from availableEventTypes.

Authentication: bearerAuth
Required scopes: `webhook:update`
Allowed roles: `owner`, `admin`
Authorization: Update webhook settings.

## Request body

- `application/json`: `UpdateWebhookSettingsRequest` required.

## Responses

- `200`: Updated webhook settings
- `default`: Error

# Delete webhook settings

**DELETE** `/webhooks`

Disables and removes the current tenant webhook configuration. Attempt logs remain visible for the retention window.

Authentication: bearerAuth
Required scopes: `webhook:delete`
Allowed roles: `owner`, `admin`
Authorization: Delete webhook settings.

## Responses

- `204`: Webhook settings deleted
- `default`: Error

# Verify webhook delivery

**POST** `/webhooks/verify`

Sends a signed synthetic webhook test event to the supplied URL or the saved webhook URL.

Authentication: bearerAuth
Required scopes: `webhook:update`
Allowed roles: `owner`, `admin`
Authorization: Verify webhook delivery.

## Request body

- `application/json`: `VerifyWebhookRequest` optional.

## Responses

- `200`: Verification result
- `default`: Error

# List webhook delivery attempts

**GET** `/webhooks/attempts`

Returns newest-first webhook delivery attempts from the last 7 days. Use this for delivery debugging, not long-term audit.

Authentication: bearerAuth
Required scopes: `webhook:read`
Allowed roles: `owner`, `admin`
Authorization: List webhook delivery attempts.

## Parameters

- `limit` (query integer, optional)
- `pageToken` (query string, optional)
- `eventType` (query WebhookEventName, optional)
- `status` (query WebhookAttemptStatus, optional)

## Responses

- `200`: Webhook delivery attempts
- `default`: Error

# List webhook signing keys for the current tenant

**GET** `/webhooks/signing-keys`

Returns the active and any expiring (mid-rotation) webhook signing keys.
On the very first call for a tenant, a key is bootstrapped automatically
and its plaintext is included in the response — exactly once. Subsequent
calls return metadata only (no plaintext).

Authentication: bearerAuth
Required scopes: `webhook:read`
Allowed roles: `owner`, `admin`
Authorization: List webhook signing keys.

## Responses

- `200`: List of signing keys (plaintext present only on bootstrap response)
- `default`: Error

# Rotate the webhook signing key

**POST** `/webhooks/signing-keys/rotate`

Generates a new signing key for webhook deliveries and demotes the previous
active key to "expiring" with a 24h overlap. During the overlap, both
keys are valid signers; receivers can update at their own pace.
Returns the plaintext of the newly-generated key exactly once.

Authentication: bearerAuth
Required scopes: `webhook:update`
Allowed roles: `owner`, `admin`
Authorization: Rotate a webhook signing key.

## Responses

- `200`: New key generated; plaintext returned exactly once
- `default`: Error

# Webhook payloads

**POST** `{configured webhook URL}`

Endpoint shape implemented by external webhook receivers. Return any 2xx response after accepting an event.

Authentication: standardWebhooks
Authorization: External receiver endpoint shape for webhook events.

## Parameters

- `webhook-id` (header string, required) - Stable event identifier. Deduplicate deliveries with this value.
- `webhook-timestamp` (header integer, required) - Unix timestamp in seconds for the delivery attempt. Reject stale timestamps to prevent replay.
- `webhook-signature` (header string, required) - Standard Webhooks signature header. Verify it against `webhook-id`, `webhook-timestamp`, and the exact raw request body.

## Request body

- `application/json`: `WebhookPayload` required.

## Payload variants

- `contact.created` (ContactCreatedWebhookEvent) Required fields: `id`, `type`, `timestamp`, `data`.
- `contact.updated` (ContactUpdatedWebhookEvent) Required fields: `id`, `type`, `timestamp`, `data`.
- `contact.archived` (ContactArchivedWebhookEvent) Required fields: `id`, `type`, `timestamp`, `data`.
- `conversation.started` (ConversationStartedWebhookEvent) Required fields: `id`, `type`, `timestamp`, `data`.
- `conversation.updated` (ConversationUpdatedWebhookEvent) Required fields: `id`, `type`, `timestamp`, `data`.
- `conversation.closed` (ConversationClosedWebhookEvent) Required fields: `id`, `type`, `timestamp`, `data`.
- `message.created` (MessageCreatedWebhookEvent) Required fields: `id`, `type`, `timestamp`, `data`.
- `message.updated` (MessageUpdatedWebhookEvent) Required fields: `id`, `type`, `timestamp`, `data`.

## Responses

- `2XX`: Event accepted
