Reply to a ticket

.md

Answer the trader, or leave a note only the team reads, with text, a picture or both.

Scopesadmin:write
Permissionview_ticketsmanage_tickets
POST/v1/admin/tickets/{ticket}/messages

An answer to the trader, or with internal a note for the team. The same as the reply box on the website, under the same rules.

An answer moves the ticket to awaiting_trader and restarts the clock on a quiet ticket. The team's first answer switches the assistant off for good on this ticket, and takes the ticket when nobody holds it yet: the first reply takes it. The trader hears of it the way any reply tells them: the bell, a toast, and an email when they are away and the firm has that mail switched on.

A note touches none of that. Not the status, not the assistant, not who holds the ticket, and the trader never sees it.

A picture travels as base64 inside JSON, like every upload in this API. Its type is worked out from the bytes, never from the name: a JPEG, PNG, GIF or WebP, at most 10 MB once decoded. It is stored re-encoded as WebP, at most 1600 pixels on its longest side.

Needs a firm. Our own staff name one with the Propper-Workspace header.

Request

FieldDescription
bodyThe text, up to 5000 characters. Required without a picture
imageA picture, as name (up to 255 characters, what the trader sees it called) and data (base64)
internaltrue for a note only the team reads. Defaults to false
curl -X POST "https://api.proppertrading.com/v1/admin/tickets/tkt_cbqjrnz84cq45ca7/messages" \
  -H "Authorization: Bearer ptat_9f2c1a44_3b8e7d2f5c9a1b4e6d8f0a2c4e6b8d1f" \
  -H "Propper-Workspace: acme" \
  -H "Idempotency-Key: 4e6a8c0b-2d4f-4b6a-8c1e-3f5a7b9d0c2e" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi Leila, a larger account is a new challenge. This is where you find them.",
    "image": { "name": "challenges.png", "data": "iVBORw0KGgoAAAANSUhEUgAAACgAAAAY..." }
  }'

Response

201, with the message as the conversation shows it:

{
    "data": {
        "object": "ticket_message",
        "id": "tmsg_ua7ypkjx6kas5gvf",
        "ticket_id": "tkt_cbqjrnz84cq45ca7",
        "from": "admin",
        "author": "Ios Tester",
        "body": "Hi Leila, a larger account is a new challenge. This is where you find them.",
        "image_url": "/v1/admin/tickets/tkt_cbqjrnz84cq45ca7/messages/tmsg_ua7ypkjx6kas5gvf/image",
        "image_name": "challenges.png",
        "internal": false,
        "streaming": false,
        "via_email": false,
        "event": null,
        "read_at": null,
        "created_at": "2026-10-15T22:29:42+00:00"
    },
    "meta": {
        "request_id": "req_01m50tyz4fewz8hhjt83b9p60g"
    }
}

Retrying with the same Idempotency-Key answers the same message and sends nothing. A second message is a new key.

Errors

StatusCodeMeaning
400workspace_requiredAsked by our own staff without naming a firm
400missing_idempotency_keyA POST without an Idempotency-Key header
400invalid_imageimage.data is not base64, is empty, or is not a JPEG, PNG, GIF or WebP
400image_too_largeThe picture is more than 10 MB once decoded
401invalid_tokenNot a token, or the secret does not match
401revoked_tokenSigned out here or from another device
401expired_tokenNinety days without use, or a set expiry passed
403admin_access_requiredThis account no longer helps run the environment
403insufficient_scopeThis device does not hold admin:write
403workspace_not_permittedThe Propper-Workspace header names an environment this account cannot act in
403forbiddenWithout view_tickets and manage_tickets
404resource_not_foundNo ticket with that id in this firm
409ticket_endedThe ticket was resolved or closed. A new question becomes a new ticket
422validation_failedNo body and no picture, a body that is too long, or an image without name or data