Conversations

For instructions on how to authenticate to use this endpoint, see API overview.

Endpoints

POST
GET
GET
POST
PATCH
DELETE
POST
POST
POST
POST
GET
GET
POST
GET
PATCH
DELETE

Create conversations tickets ai human outcome

Record that a human used or edited the latest AI draft.

Required API key scopes

ticket:write

Path parameters

  • id
    string

Request parameters

  • message_id
    string
  • outcome

Response


Example request

POST /api/projects/:project_id/conversations/tickets/:id/ai_human_outcome
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/:id/ai_human_outcome/\
-d message_id="string",\
-d outcome=undefined

Example response

Status 202
RESPONSE
{
"message_id": "string",
"outcome": "used"
}
Status 409
RESPONSE
{
"detail": "string",
"error_type": "string"
}

List all conversations tickets messages

Return the message thread for a ticket, ordered chronologically (paginated).

Required API key scopes

ticket:read

Path parameters

  • id
    string

Query parameters

  • limit
    integer
  • offset
    integer

Response


Example request

GET /api/projects/:project_id/conversations/tickets/:id/messages
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/:id/messages/

Example response

Status 200
RESPONSE
{
"count": 123,
"next": "http://api.example.org/accounts/?offset=400&limit=100",
"previous": "http://api.example.org/accounts/?offset=200&limit=100",
"results": [
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"message_type": "customer_message",
"content": "string",
"rich_content": null,
"author_type": "string",
"author_name": "string",
"author_email": "user@example.com",
"is_private": true,
"has_full_email_content": true,
"version": 0,
"created_at": "2019-08-24T14:15:22Z"
}
]
}

Retrieve conversations tickets messages full email

Return the full inbound email body in Markdown.

Required API key scopes

ticket:read

Path parameters

  • id
    string
  • message_id
    string

Response


Example request

GET /api/projects/:project_id/conversations/tickets/:id/messages/:message_id/full_email
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/:id/messages/:message_id/full_email/

Example response

Status 200
RESPONSE
{
"content": "string"
}
Status 404
RESPONSE
{
"detail": "string",
"error_type": "string"
}

Create conversations tickets notes

Add a private note to a ticket.

The note is visible to your team only. The request has no privacy field, so this endpoint never sends anything to the customer.

Required API key scopes

ticket:write

Path parameters

  • id
    string

Request parameters

  • message
    string
  • rich_content

Response


Example request

POST /api/projects/:project_id/conversations/tickets/:id/notes
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/:id/notes/\
-d message="string"

Example response

Status 200 An identical note was already posted by a recent request. The original note is returned and nothing new is written.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"message_type": "customer_message",
"content": "string",
"rich_content": null,
"author_type": "string",
"author_name": "string",
"author_email": "user@example.com",
"is_private": true,
"has_full_email_content": true,
"version": 0,
"created_at": "2019-08-24T14:15:22Z"
}
Status 201
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"message_type": "customer_message",
"content": "string",
"rich_content": null,
"author_type": "string",
"author_name": "string",
"author_email": "user@example.com",
"is_private": true,
"has_full_email_content": true,
"version": 0,
"created_at": "2019-08-24T14:15:22Z"
}
Status 400
RESPONSE
{
"detail": "string",
"error_type": "string"
}
Status 409 An identical note is still being created by another request.
RESPONSE
{
"detail": "string",
"error_type": "string"
}

Update conversations tickets notes

Update a private note on a ticket.

Only the note's author can edit it. Customer-facing replies cannot be edited (outbound delivery only runs on create).

Required API key scopes

ticket:write

Path parameters

  • id
    string
  • message_id
    string

Request parameters

  • message
    string
  • rich_content

Response


Example request

PATCH /api/projects/:project_id/conversations/tickets/:id/notes/:message_id
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl -X PATCH \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/:id/notes/:message_id/\
-d message="string"

Example response

Status 200
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"message_type": "customer_message",
"content": "string",
"rich_content": null,
"author_type": "string",
"author_name": "string",
"author_email": "user@example.com",
"is_private": true,
"has_full_email_content": true,
"version": 0,
"created_at": "2019-08-24T14:15:22Z"
}
Status 400
RESPONSE
{
"detail": "string",
"error_type": "string"
}
Status 403
RESPONSE
{
"detail": "string",
"error_type": "string"
}
Status 404
RESPONSE
{
"detail": "string",
"error_type": "string"
}

Delete conversations tickets notes

Soft-delete a private note on a ticket.

Only the note's author can delete it. Customer-facing replies cannot be deleted via this endpoint.

Required API key scopes

ticket:write

Path parameters

  • id
    string
  • message_id
    string

Example request

DELETE /api/projects/:project_id/conversations/tickets/:id/notes/:message_id
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl -X DELETE \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/:id/notes/:message_id/

Example response

Status 204 Note soft-deleted.
Status 400
RESPONSE
{
"detail": "string",
"error_type": "string"
}
Status 403
RESPONSE
{
"detail": "string",
"error_type": "string"
}
Status 404
RESPONSE
{
"detail": "string",
"error_type": "string"
}

Create conversations tickets reply

Post a reply or internal note to a ticket.

With is_private=false, the reply is delivered to the customer via the ticket's channel (email, Slack, Teams, GitHub). With is_private=true, the message is stored as an internal note only visible to team members.

Retrying an identical message from the same author within a short window returns the original message with a 200 rather than posting it twice, and a 409 while a concurrent request is still creating it.

Required API key scopes

ticket:write

Path parameters

  • id
    string

Request parameters

  • message
    string
  • is_private
    boolean
    Default: false
  • rich_content

Response


Example request

POST /api/projects/:project_id/conversations/tickets/:id/reply
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/:id/reply/\
-d message="string"

Example response

Status 200 An identical message was already posted by a recent request. The original message is returned and nothing new is written.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"message_type": "customer_message",
"content": "string",
"rich_content": null,
"author_type": "string",
"author_name": "string",
"author_email": "user@example.com",
"is_private": true,
"has_full_email_content": true,
"version": 0,
"created_at": "2019-08-24T14:15:22Z"
}
Status 201
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"message_type": "customer_message",
"content": "string",
"rich_content": null,
"author_type": "string",
"author_name": "string",
"author_email": "user@example.com",
"is_private": true,
"has_full_email_content": true,
"version": 0,
"created_at": "2019-08-24T14:15:22Z"
}
Status 400
RESPONSE
{
"detail": "string",
"error_type": "string"
}
Status 409 An identical message is still being created by another request.
RESPONSE
{
"detail": "string",
"error_type": "string"
}

Create conversations tickets bulk update status

Update the status of multiple tickets in a single request.

Only tickets belonging to the current team are affected; other-team UUIDs are silently ignored. Tickets the caller lacks editor-level access to (denied or view-only via object-level access control) are silently skipped too, the same way single-ticket updates enforce object-level access via get_object(). Tickets already in the requested status are skipped.

Request parameters

  • ids
    array
  • status

Response


Example request

POST /api/projects/:project_id/conversations/tickets/bulk_update_status
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/bulk_update_status/\
-d ids="array",\
-d status=undefined

Example response

Status 200
RESPONSE
{
"updated": 0,
"ids": [
"497f6eca-6276-4993-bfeb-53cbbbba6f08"
]
}

Create conversations tickets bulk update tags

Bulk update tags on multiple objects.

PAT access: this action has no required_scopes= on the decorator — inheriting viewsets must add "bulk_update_tags" to their scope_object_write_actions list to accept personal API keys. Without that opt-in, APIScopePermission rejects PAT requests with "This action does not support personal API key access". Done per-viewset so granting <scope>:write for one resource doesn't leak access to sibling resources that share this mixin.

Accepts:

  • {"ids": [...], "action": "add"|"remove"|"set", "tags": ["tag1", "tag2"]}

Actions:

  • "add": Add tags to existing tags on each object
  • "remove": Remove specific tags from each object
  • "set": Replace all tags on each object with the provided list

Request parameters

  • ids
    array
  • action
  • tags
    array

Response


Example request

POST /api/projects/:project_id/conversations/tickets/bulk_update_tags
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/bulk_update_tags/\
-d ids="array",\
-d action=undefined,\
-d tags="array"

Example response

Status 200
RESPONSE
{
"updated": [
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"tags": [
"string"
]
}
],
"skipped": [
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"reason": "string"
}
]
}

Create conversations tickets compose

Create a new outbound ticket and send the first message to the customer.

Idempotent within a short window: an identical compose retried while the first is still in flight returns 409, and one retried after it committed returns the same ticket with a 200. Only a genuinely new request creates a ticket and emails the customer.

Required API key scopes

ticket:write

Request parameters

  • recipient_email
    string
  • recipient_distinct_id
    string
  • email_subject
    string
  • email_config_id
    string
  • message
    string
  • rich_content
  • tags
    array

Response


Example request

POST /api/projects/:project_id/conversations/tickets/compose
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/compose/\
-d recipient_email="string",\
-d email_config_id="string",\
-d message="string"

Example response

Status 200 An identical compose was already handled; the existing ticket is returned.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ticket_number": 0
}
Status 201 Ticket created.
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"ticket_number": 0
}
Status 400
RESPONSE
{
"detail": "string",
"error_type": "string"
}
Status 409 An identical compose is still being created by another request.
RESPONSE
{
"detail": "string",
"error_type": "string"
}

Retrieve conversations tickets unread count

Get total unread ticket count for the team.

Returns the sum of unread_team_count for all non-resolved tickets visible to the caller. The team-wide Redis cache (30s TTL, invalidated on changes) is only used for callers without object-level ticket restrictions, since it holds one unscoped total per team - serving it to a restricted member would leak counts for tickets they can't see.

Required API key scopes

ticket:read

Response


Example request

GET /api/projects/:project_id/conversations/tickets/unread_count
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/tickets/unread_count/

Example response

Status 200
RESPONSE
{
"count": 0
}

List all conversations views

Required API key scopes

ticket:read

Query parameters

  • limit
    integer
  • offset
    integer

Response


Example request

GET /api/projects/:project_id/conversations/views
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/views/

Example response

Status 200
RESPONSE
{
"count": 123,
"next": "http://api.example.org/accounts/?offset=400&limit=100",
"previous": "http://api.example.org/accounts/?offset=200&limit=100",
"results": [
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"short_id": "string",
"name": "string",
"filters": {
"status": [
"new"
],
"priority": [
"low"
],
"channel": "widget",
"sla": "breached",
"aiTriageResult": [
"persisted"
],
"assignee": [
"me"
],
"tags": [
"string"
],
"tagsMatch": "any",
"tagsExclude": [
"string"
],
"dateFrom": "string",
"dateTo": "string",
"sorting": {
"columnKey": "string",
"order": 1
},
"search": "string"
},
"created_at": "2019-08-24T14:15:22Z",
"created_by": {
"id": 0,
"uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
"distinct_id": "string",
"first_name": "string",
"last_name": "string",
"email": "user@example.com",
"is_email_verified": true,
"hedgehog_config": {},
"role_at_organization": "engineering"
},
"is_favorited": true
}
]
}

Create conversations views

Required API key scopes

ticket:write

Request parameters

  • name
    string
  • filters
  • is_favorited
    boolean

Response


Example request

POST /api/projects/:project_id/conversations/views
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl
-H 'Content-Type: application/json'\
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/views/\
-d name="string"

Example response

Status 201
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"short_id": "string",
"name": "string",
"filters": {
"status": [
"new"
],
"priority": [
"low"
],
"channel": "widget",
"sla": "breached",
"aiTriageResult": [
"persisted"
],
"assignee": [
"me"
],
"tags": [
"string"
],
"tagsMatch": "any",
"tagsExclude": [
"string"
],
"dateFrom": "string",
"dateTo": "string",
"sorting": {
"columnKey": "string",
"order": 1
},
"search": "string"
},
"created_at": "2019-08-24T14:15:22Z",
"created_by": {
"id": 0,
"uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
"distinct_id": "string",
"first_name": "string",
"last_name": "string",
"email": "user@example.com",
"is_email_verified": true,
"hedgehog_config": {},
"role_at_organization": "engineering"
},
"is_favorited": true
}

Retrieve conversations views

Required API key scopes

ticket:read

Path parameters

  • short_id
    string

Response


Example request

GET /api/projects/:project_id/conversations/views/:short_id
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/views/:short_id/

Example response

Status 200
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"short_id": "string",
"name": "string",
"filters": {
"status": [
"new"
],
"priority": [
"low"
],
"channel": "widget",
"sla": "breached",
"aiTriageResult": [
"persisted"
],
"assignee": [
"me"
],
"tags": [
"string"
],
"tagsMatch": "any",
"tagsExclude": [
"string"
],
"dateFrom": "string",
"dateTo": "string",
"sorting": {
"columnKey": "string",
"order": 1
},
"search": "string"
},
"created_at": "2019-08-24T14:15:22Z",
"created_by": {
"id": 0,
"uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
"distinct_id": "string",
"first_name": "string",
"last_name": "string",
"email": "user@example.com",
"is_email_verified": true,
"hedgehog_config": {},
"role_at_organization": "engineering"
},
"is_favorited": true
}

Update conversations views

Required API key scopes

ticket:write

Path parameters

  • short_id
    string

Request parameters

  • name
    string
  • filters
  • is_favorited
    boolean

Response


Example request

PATCH /api/projects/:project_id/conversations/views/:short_id
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl -X PATCH \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/views/:short_id/\
-d name="string"

Example response

Status 200
RESPONSE
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"short_id": "string",
"name": "string",
"filters": {
"status": [
"new"
],
"priority": [
"low"
],
"channel": "widget",
"sla": "breached",
"aiTriageResult": [
"persisted"
],
"assignee": [
"me"
],
"tags": [
"string"
],
"tagsMatch": "any",
"tagsExclude": [
"string"
],
"dateFrom": "string",
"dateTo": "string",
"sorting": {
"columnKey": "string",
"order": 1
},
"search": "string"
},
"created_at": "2019-08-24T14:15:22Z",
"created_by": {
"id": 0,
"uuid": "095be615-a8ad-4c33-8e9c-c7612fbf6c9f",
"distinct_id": "string",
"first_name": "string",
"last_name": "string",
"email": "user@example.com",
"is_email_verified": true,
"hedgehog_config": {},
"role_at_organization": "engineering"
},
"is_favorited": true
}

Delete conversations views

Required API key scopes

ticket:write

Path parameters

  • short_id
    string

Example request

DELETE /api/projects/:project_id/conversations/views/:short_id
export POSTHOG_PERSONAL_API_KEY=[your personal api key]
curl -X DELETE \
-H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \
<ph_app_host>/api/projects/:project_id/conversations/views/:short_id/

Example response

Status 204 No response body