For the complete documentation index, see llms.txt. This page is also available as Markdown.

Messages

Edit a message

patch
/issues/{id}/messages/{message_id}

Replaces the body of a message and syncs the edit to its connected channel (Slack, Microsoft Teams, Discord, Google Chat, Telegram, or the chat widget). Only messages authored by the API token's user can be edited. Emails, customer messages, and redacted messages cannot be edited. Callers without issue write access can edit only their own internal notes.

Rate limit: 30 requests per minute

Authorizations
AuthorizationstringRequired

Authorization: Bearer

Path parameters
idstringRequired

The ID of the issue that the message belongs to.

message_idstringRequired

The ID of the message to edit.

Body
body_htmlstringRequired

The new message body in HTML. Replaces the existing body.

Responses
200Success
application/json
request_idstringRequired

The request ID for tracking.

patch/issues/{id}/messages/{message_id}
PATCH /issues/{id}/messages/{message_id} HTTP/1.1
Host: api.usepylon.com
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 20

{
  "body_html": "text"
}
{
  "data": {
    "author": {
      "avatar_url": "text",
      "contact": {
        "email": "text",
        "id": "text"
      },
      "name": "text",
      "user": {
        "email": "text",
        "id": "text"
      }
    },
    "author_unverified": true,
    "email_info": {
      "bcc_emails": [
        "text"
      ],
      "cc_emails": [
        "text"
      ],
      "from_email": "text",
      "message_id": "text",
      "opens": [
        {
          "email": "text",
          "time": "2026-09-30T16:33:54Z"
        }
      ],
      "sender_authentication_status": "pass",
      "to_emails": [
        "text"
      ]
    },
    "file_urls": [
      "text"
    ],
    "id": "text",
    "is_private": true,
    "message_html": "text",
    "slack_info": {
      "channel_id": "text",
      "message_ts": "text",
      "permalink": "text",
      "thread_ts": "text"
    },
    "source": "text",
    "thread_id": "text",
    "timestamp": "2026-01-01T00:00:00.000Z"
  },
  "request_id": "text"
}

Get messages on an issue

get
/issues/{id}/messages

Returns messages for the specified issue, including replies and internal notes, ordered from oldest to newest.

To post a reply, use the message's top-level id—not thread_id or email_info.message_id. To post an internal note, use an internal thread's thread_id or an internal note's top-level id. Omit both limit and cursor to return all messages; when cursor is provided without limit, the page size defaults to 100.

Rate limit: 120 requests per minute

Authorizations
AuthorizationstringRequired

Authorization: Bearer

Path parameters
idstringRequired

The ID of the issue to fetch messages for.

Query parameters
cursorstringOptional

The cursor to use for pagination.

limitinteger · int64Optional

The number of messages to fetch. Must be greater than 0 and at most 1000. If omitted, all messages are returned for backward compatibility.

Responses
200Success
application/json
request_idstringRequired

The request ID for tracking.

get/issues/{id}/messages
GET /issues/{id}/messages HTTP/1.1
Host: api.usepylon.com
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*
{
  "data": [
    {
      "author": {
        "avatar_url": "text",
        "contact": {
          "email": "text",
          "id": "text"
        },
        "name": "text",
        "user": {
          "email": "text",
          "id": "text"
        }
      },
      "author_unverified": true,
      "email_info": {
        "bcc_emails": [
          "text"
        ],
        "cc_emails": [
          "text"
        ],
        "from_email": "text",
        "message_id": "text",
        "opens": [
          {
            "email": "text",
            "time": "2026-09-30T16:33:54Z"
          }
        ],
        "sender_authentication_status": "pass",
        "to_emails": [
          "text"
        ]
      },
      "file_urls": [
        "text"
      ],
      "id": "text",
      "is_private": true,
      "message_html": "text",
      "slack_info": {
        "channel_id": "text",
        "message_ts": "text",
        "permalink": "text",
        "thread_ts": "text"
      },
      "source": "text",
      "thread_id": "text",
      "timestamp": "2026-01-01T00:00:00.000Z"
    }
  ],
  "pagination": {
    "cursor": "text",
    "has_next_page": true
  },
  "request_id": "text"
}

Get all threads on an issue

get
/issues/{id}/threads

Returns all threads for the specified issue.

Rate limit: 120 requests per minute

Authorizations
AuthorizationstringRequired

Authorization: Bearer

Path parameters
idstringRequired

The ID of the issue to fetch threads for.

Responses
200Success
application/json
request_idstringRequired

The request ID for tracking.

get/issues/{id}/threads
GET /issues/{id}/threads HTTP/1.1
Host: api.usepylon.com
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*
{
  "data": [
    {
      "channel_id": "text",
      "id": "text",
      "issue_id": "text",
      "name": "text",
      "source": "text",
      "thread_id": "text"
    }
  ],
  "request_id": "text"
}

Create a thread on an issue

post
/issues/{id}/threads

Creates a new internal thread on an issue.

Rate limit: 120 requests per minute

Authorizations
AuthorizationstringRequired

Authorization: Bearer

Path parameters
idstringRequired

The ID of the issue to create a thread for.

Body
namestringOptional

The name of the thread.

slack_channel_idstringOptional

The Slack channel ID to sync the thread to. When slack_thread_ts is also provided, links that existing Slack thread. Otherwise, creates a new Slack-backed thread. Linking an existing Slack thread imports its message history and enables ongoing synchronization. Creating or linking a Slack-backed thread requires an API token configured to act as a human Pylon user whose verified Slack identity is a member of the channel. Pylon bot users and standalone API identities are not supported because they do not have Slack-verifiable channel membership.

slack_thread_tsstringOptional

The root message timestamp of an existing Slack thread to link. Requires slack_channel_id.

Responses
200Success
application/json
request_idstringRequired

The request ID for tracking.

post/issues/{id}/threads
POST /issues/{id}/threads HTTP/1.1
Host: api.usepylon.com
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 66

{
  "name": "text",
  "slack_channel_id": "text",
  "slack_thread_ts": "text"
}
{
  "data": {
    "channel_id": "text",
    "id": "text",
    "issue_id": "text",
    "name": "text",
    "source": "text",
    "thread_id": "text"
  },
  "request_id": "text"
}

Reply to an issue

post
/issues/{id}/reply

Sends a customer-facing reply on an issue, visible to the requester.

message_id is required to identify which existing customer-visible conversation or thread to continue. Use the top-level message id returned by GET /issues/{id}/messages; do not use an external provider's message ID or the nested email_info.message_id value.

For an email conversation, email_info must contain at least one recipient across to_emails, cc_emails, and bcc_emails. The referenced message controls email threading, but its recipients are not copied automatically.

Rate limit: 30 requests per minute

Authorizations
AuthorizationstringRequired

Authorization: Bearer

Path parameters
idstringRequired

The ID of the issue.

Body
attachment_urlsstring[]Optional

URLs of files to attach to this reply.

body_htmlstringRequired

The body of the message in HTML.

contact_idstringOptional

Optional contact ID to post the message as. Only one of user_id or contact_id can be provided.

message_idstringRequired

The top-level Pylon message ID to reply to, from the id field returned by GET /issues/{id}/messages. The message must belong to the issue in the request path and must be customer-visible, not an internal note. This selects the external conversation or thread where the reply is delivered. For email, it also sets the In-Reply-To and References chain. Do not use a provider-specific message ID or email_info.message_id.

user_idstringOptional

Optional user ID to post the message as. Only one of user_id or contact_id can be provided.

Responses
200Success
application/json
request_idstringRequired

The request ID for tracking.

post/issues/{id}/reply
POST /issues/{id}/reply HTTP/1.1
Host: api.usepylon.com
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 322

{
  "attachment_urls": [
    "text"
  ],
  "body_html": "text",
  "contact_id": "text",
  "custom_source": {
    "created_at": "2026-01-01T00:00:00.000Z",
    "external_id": "text",
    "metadata": {
      "conversation_id": "conv_123",
      "source": "wechat"
    }
  },
  "email_info": {
    "bcc_emails": [
      "text"
    ],
    "cc_emails": [
      "text"
    ],
    "to_emails": [
      "text"
    ]
  },
  "message_id": "text",
  "user_id": "text"
}
{
  "data": {
    "id": "text",
    "issue_id": "text"
  },
  "request_id": "text"
}

Create or update a draft reply

post
/issues/{id}/draft-reply

Creates a private customer-reply draft without sending it. The draft is visible only to its owner and may require a normal issue refetch before appearing in the app. For unlinked or service API tokens, user_id is required. This endpoint updates only untouched drafts that it previously created; every other existing draft returns a conflict.

Rate limit: 30 requests per minute

Authorizations
AuthorizationstringRequired

Authorization: Bearer

Path parameters
idstringRequired

The ID of the issue.

Body
bcc_emailsstring[]Optional

The draft's BCC recipients. See to_emails.

body_htmlstringRequired

The draft body in HTML.

cc_emailsstring[]Optional

The draft's CC recipients. See to_emails.

to_emailsstring[]Optional

The draft's To recipients. If any of to_emails, cc_emails, or bcc_emails is provided, the draft uses exactly those lists and an omitted list is saved empty; omit all three to use the composer's default recipients.

user_idstringOptional

The active Pylon user who should own and see the draft. Defaults to the API-token user when it is a human Pylon user; required for unlinked or service API tokens.

Responses
200Success
application/json
request_idstringRequired

The request ID for tracking.

post/issues/{id}/draft-reply
POST /issues/{id}/draft-reply HTTP/1.1
Host: api.usepylon.com
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 101

{
  "bcc_emails": [
    "text"
  ],
  "body_html": "text",
  "cc_emails": [
    "text"
  ],
  "to_emails": [
    "text"
  ],
  "user_id": "text"
}
{
  "data": {
    "id": "text",
    "issue_id": "text",
    "status": "text",
    "updated_at": "2026-01-01T00:00:00.000Z",
    "user_id": "text"
  },
  "request_id": "text"
}

Create an internal note on an issue

post
/issues/{id}/note

Posts an internal note on an issue thread. Not visible to the requester.

Thread selection:

  • If thread_id is provided, posts to that internal thread.

  • If message_id is provided, posts to the thread containing that internal note.

  • Providing both thread_id and message_id returns a 400 response.

  • If neither is provided, posts to the most recently created Slack-backed internal thread. If none exists, creates a Pylon-only internal thread.

  • thread_name names that newly created Pylon-only thread and is otherwise ignored.

Empty thread_id, message_id, and thread_name values are treated as omitted.

Rate limit: 30 requests per minute

Authorizations
AuthorizationstringRequired

Authorization: Bearer

Path parameters
idstringRequired

The ID of the issue.

Body
attachment_urlsstring[]Optional

URLs of files to attach to this internal note.

body_htmlstringRequired

The body of the message in HTML.

message_idstringOptional

The ID of an internal note whose thread should receive the new note. Use the top-level id from GET /issues/{id}/messages. Cannot be combined with thread_id.

thread_idstringOptional

The ID of the internal thread to post the note to. Use the id field (not thread_id) from GET /issues/{id}/threads. Cannot be combined with message_id.

thread_namestringOptional

Optional name for a new Pylon-only internal thread. Used only when neither thread_id nor message_id is provided and no Slack-backed internal thread exists.

user_idstringOptional

Optional user ID to post the message as. If not provided, the API token user will be used.

Responses
200Success
application/json
request_idstringRequired

The request ID for tracking.

post/issues/{id}/note
POST /issues/{id}/note HTTP/1.1
Host: api.usepylon.com
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 124

{
  "attachment_urls": [
    "text"
  ],
  "body_html": "text",
  "message_id": "text",
  "thread_id": "text",
  "thread_name": "text",
  "user_id": "text"
}
{
  "data": {
    "id": "text",
    "issue_id": "text"
  },
  "request_id": "text"
}

Redact a message

post
/issues/{id}/messages/{message_id}/redact

Permanently replaces a message's stored content with redacted_body_message and optionally deletes its attachments. This action cannot be undone. When omitted, redacted_body_message defaults to "This message has been redacted." and redact_attachments defaults to true.

Rate limit: 120 requests per minute

Authorizations
AuthorizationstringRequired

Authorization: Bearer

Path parameters
idstringRequired

The ID of the issue that the message belongs to.

message_idstringRequired

The ID of the message to redact.

Body
redact_attachmentsbooleanOptional

Whether to permanently delete the message's attachments. Defaults to true.

redacted_body_messagestringOptional

Replacement message body in HTML. The caller is responsible for replacing any sensitive portions. Defaults to "This message has been redacted."

Responses
200Success
application/json
request_idstringRequired

The request ID for tracking.

post/issues/{id}/messages/{message_id}/redact
POST /issues/{id}/messages/{message_id}/redact HTTP/1.1
Host: api.usepylon.com
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 58

{
  "redact_attachments": true,
  "redacted_body_message": "text"
}
{
  "data": {
    "author": {
      "avatar_url": "text",
      "contact": {
        "email": "text",
        "id": "text"
      },
      "name": "text",
      "user": {
        "email": "text",
        "id": "text"
      }
    },
    "author_unverified": true,
    "email_info": {
      "bcc_emails": [
        "text"
      ],
      "cc_emails": [
        "text"
      ],
      "from_email": "text",
      "message_id": "text",
      "opens": [
        {
          "email": "text",
          "time": "2026-09-30T16:33:54Z"
        }
      ],
      "sender_authentication_status": "pass",
      "to_emails": [
        "text"
      ]
    },
    "file_urls": [
      "text"
    ],
    "id": "text",
    "is_private": true,
    "message_html": "text",
    "slack_info": {
      "channel_id": "text",
      "message_ts": "text",
      "permalink": "text",
      "thread_ts": "text"
    },
    "source": "text",
    "thread_id": "text",
    "timestamp": "2026-01-01T00:00:00.000Z"
  },
  "request_id": "text"
}

Delete a message

delete
/issues/{id}/messages/{message_id}

Permanently deletes a message from an issue and its connected external system.

Rate limit: 30 requests per minute

Authorizations
AuthorizationstringRequired

Authorization: Bearer

Path parameters
idstringRequired

The ID of the issue that the message belongs to.

message_idstringRequired

The ID of the message to delete.

Responses
200Success
application/json
request_idstringRequired

The request ID for tracking.

delete/issues/{id}/messages/{message_id}
DELETE /issues/{id}/messages/{message_id} HTTP/1.1
Host: api.usepylon.com
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*
{
  "request_id": "text"
}

Last updated

Was this helpful?