Message endpoints

Read a channel's messages page by page, send messages, edit and delete them through the REST API, and understand the message object and pills.

Updated

Paths are relative to https://api.fishcamp.space/api/v1 and all need the X-Workspace-Id header. See the REST API overview for authentication and errors.

The message object

{
  "id": "6c1f0f0e-8a7e-4f4e-9d55-0e6f6f1a2b3c",
  "channelId": "0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10",
  "kind": "TEXT",
  "eventType": null,
  "authorType": "MEMBER",
  "author": {
    "id": "7a1c2e3f-4b5d-4e6f-8a9b-0c1d2e3f4a5b",
    "name": "Maya Lopes",
    "email": "maya@northstar.studio",
    "avatarUrl": null
  },
  "authorName": null,
  "authorAvatarUrl": null,
  "content": "@Theo the build for 2.4 is green, can you check the release notes?",
  "payload": null,
  "embeds": [],
  "createdAt": "2026-11-05T14:32:08.000Z",
  "editedAt": null,
  "deletedAt": null,
  "clientId": null
}
FieldDescription
kindTEXT for messages, EVENT for timeline pills.
eventTypeFor pills, what happened (see Pills). null for messages.
authorTypeMEMBER (a person), AGENT (an API key), WEBHOOK (an incoming webhook) or SYSTEM (Fishcamp itself). New values may be added: show unknown ones like SYSTEM.
authorThe person who wrote it, or null when it wasn't a person.
authorNameThe name shown when there's no person, for example the webhook's name.
contentThe text, up to 4,000 characters. Empty for deleted messages.
payloadExtra data: details of a pill (such as a call's duration) or, for webhook messages, the cards sent (also in embeds). null when there's none.
embedsCards sent by incoming webhooks.
editedAt, deletedAtWhen the message was edited or deleted, or null.

List messages

GET /channels/:channelId/messages?limit=50&cursor=…
QueryDescription
limitMessages per page, from 1 to 100. Default: 50.
cursorThe nextCursor of the previous page, to get older messages. Treat it as an opaque string.

The first page has the most recent messages. Inside a page, messages go from oldest to newest, ready to display.

{
  "items": [{ "id": "…", "kind": "TEXT", "content": "…" }],
  "nextCursor": "MjAyNi0xMS0wNVQxNDozMjowOC4wMDBafDZjMWYwZjBl…"
}

Keep passing nextCursor as cursor until it comes back null:

curl "https://api.fishcamp.space/api/v1/channels/0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10/messages?limit=100" \
  -H "Authorization: Bearer cwk_YOUR_KEY" \
  -H "X-Workspace-Id: YOUR_WORKSPACE_ID"

Deleted messages stay in the list with deletedAt set and empty content, so a conversation keeps its shape.

Send a message

POST /channels/:channelId/messages
FieldTypeRequiredDescription
contentstringYes1 to 4,000 characters. Trailing spaces and line breaks are trimmed.
clientIdstringNoYour own ID for the message (8 to 64 letters, digits, _ or -). It comes back in the response, so you can match what you sent.
curl -X POST "https://api.fishcamp.space/api/v1/channels/0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10/messages" \
  -H "Authorization: Bearer cwk_YOUR_KEY" \
  -H "X-Workspace-Id: YOUR_WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{"content": "Release 2.5 is out: https://example.com/releases/2.5"}'

Returns 201 Created with the message. Everyone in the channel sees it right away. Messages sent with an API key need the write scope and show the key's name with an AI label.

Edit a message

PATCH /messages/:messageId
FieldTypeRequiredDescription
contentstringYesThe new text, 1 to 4,000 characters.

Only the author can edit, and only text messages. Returns the message with editedAt set. Someone else's message gets 403 forbidden.

With an API key (write scope), "the author" means the key itself: a key edits only the messages it posted, and never the messages its creator wrote as a person.

Delete a message

DELETE /messages/:messageId

The author can delete their own messages; admins and owners, as people, can delete any message. Returns 204. Everyone sees "Message deleted" in its place.

An API key deletes only the messages it posted itself, even when an admin created it: anyone else's message gets 403 forbidden.

Pills

Pills are messages with kind: "EVENT". Their eventType says what happened:

eventTypeWhat happened
channel.createdThe channel was created.
channel.renamedThe channel was renamed (payload.from, payload.to).
channel.modality_changedVoice, camera or screen sharing was turned on or off (payload.modality, payload.enabled).
channel.member_addedSomeone was added to the channel, or joined it with an invite link (payload.via is invite_link).
call.startedThe first person joined: the audio room opened.
call.participant_joinedSomeone joined the audio room. For a guest, payload.guest is true and the name ends with "(guest)".
call.participant_leftSomeone left the audio room.
call.screen_share_startedSomeone started sharing their screen.
call.screen_share_stoppedSomeone stopped sharing their screen.
call.endedThe audio room closed (payload.durationMs, payload.participants).
webhook.createdAn admin created an incoming webhook.
webhook.revokedAn admin revoked an incoming webhook.
transcript.startedTranscription started in the audio room. New in November 2026
transcript.routedThe transcript was saved, sent to agents or discarded, failed, or is waiting for a person. New in November 2026

Pills can't be edited. Treat unknown eventType values as plain information: new ones may be added.

On this page