API docs

zpe-chat — the universe's one messaging API

mount /chat · base https://zpe.vc/chat · v1.0.0 · 8 operations · 6 write · 0 need no account · wip

archived: committed 2026-11-01, from 69440287. Contract: https://zpe.vc/chat/_/api/v1/openapi.json

GET /_/api/v1/conversations The conversations this caller holds a seat in, newest first. getConversations

The conversations this caller holds a seat in, newest first.

Parameters

nameinrequiredtypenotes
cursorquerynostringthe `next_cursor` a previous page answered; the page after that one. Absent, the listing starts at the newest conversation.
limitquerynointegerhow many conversations one page carries: 1 to 200, and 50 when the caller does not say
object_idquerynostringThe object's id in its own system. Asked with `object_kind`, and repeated on every page: `next_cursor` carries the position, not the filter.
object_kindquerynostringWith `object_id`: the conversations about one thing, for a caller that already knows what it is asking for. One without the other is refused.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The caller's conversations, newest first, one page at a time. `next_cursor` is the value to send as `cursor` for the page after this one, and null when this page holds the last conversation.object
400`limit` is not a whole number in 1 to 200, or `cursor` was not written by this app.object
401No session and no key, so there is nobody whose conversations these could be.object
403This account may not do thatobject
404Nothing is hereobject
429Too many requestsobject

Example

The conversations this caller holds a seat in, newest first., with a token from the door.

GET /_/api/v1/conversations
curl -X GET 'https://zpe.vc/chat/_/api/v1/conversations?cursor=string&limit=0&object_id=string&object_kind=string' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "conversations": [
    {}
  ],
  "next_cursor": null
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /_/api/v1/conversations Create a conversation, with the caller as its owner. postConversations

Create a conversation, with the caller as its owner.

Parameters

nameinrequiredtypenotes
Idempotency-Keyheadernostringthe caller's own retry handle. The same key created twice writes one conversation and answers the conversation id the first call answered; no display number is spent on the replay.

Request body application/json · required

fieldtyperequirednotes
kind"direct" | "group" | "object" | "public"yes
titlestring | nullno
object_kindstring | nullnoWhat this conversation is about, when kind is `object`.
object_idstring | nullno
retention_daysinteger | nullnoNull keeps the bodies; an object conversation defaults to 30.

Responses

statusmeaningbody
200That `Idempotency-Key` was already here: nothing was written, no display number was spent, and the conversation is the one the first call answered with.object
201A conversation this call created: body `{kind, object_kind?, object_id?, title?}`. Its `display_id` is this account's next number, and the caller's owner seat is written in the same batch, so the thread is readable by the person who made it.object
400The body is not one JSON object, `kind` is not one of the four, an optional field is not text, or the `Idempotency-Key` header is empty.object
401No session and no key, so there is nobody to own the conversation.object
403This account may not do thatobject
404Nothing is hereobject
429Too many requestsobject

Example

Create a conversation, with the caller as its owner., with a token from the door.

POST /_/api/v1/conversations
curl -X POST 'https://zpe.vc/chat/_/api/v1/conversations' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "kind": "direct",
  "title": null,
  "object_kind": null,
  "object_id": null,
  "retention_days": null
}'
200 application/json
{
  "conversation": {}
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /_/api/v1/conversations/{id} One conversation, its members, and a page of its messages. getConversationsById

One conversation, its members, and a page of its messages.

Parameters

nameinrequiredtypenotes
idpathyesstringthe conversation to read, as `conversation.id` spells it
beforequerynointegerexclusive upper bound: messages numbered below this are included, this one is not
limitquerynointegerhow many messages one page carries: 1 to 200, and 50 when the caller does not say
sincequerynointegerthe lowest message number to include; `next_cursor` from a previous page is exactly this value

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The conversation, its members, and the page of its messages: ascending by `seq`, `since` inclusive and `before` exclusive. `next_cursor` is the `since` that continues after this page, and null when there is no more.object
400A query value is not a whole number, or `limit` is outside 1 to 200.object
401No session and no key, so there is nobody to read as.object
403This account may not do thatobject
404No conversation this caller holds a seat in answers to that id. Never distinguished from one that does not exist.object
429Too many requestsobject

Example

One conversation, its members, and a page of its messages., with a token from the door.

GET /_/api/v1/conversations/{id}
curl -X GET 'https://zpe.vc/chat/_/api/v1/conversations/string?before=0&limit=0&since=0' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "conversation": {},
  "members": [
    {}
  ],
  "messages": [
    {}
  ],
  "next_cursor": null,
  "unread": 0
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

PATCH /_/api/v1/conversations/{id} Open or close a conversation. Its owner is the only caller who may. patchConversationsById

Open or close a conversation. Its owner is the only caller who may.

Parameters

nameinrequiredtypenotes
idpathyesstringthe conversation whose state is being set

Request body application/json · required

fieldtyperequirednotes
state"open" | "closed"yes

Responses

statusmeaningbody
200The conversation with `state` set as asked: body `{"state": "open" | "closed"}`. A closed conversation refuses new messages with `closed` until its owner reopens it.object
400The body is not one JSON object, or `state` is neither `open` nor `closed`.object
401No session and no key, so there is nobody whose ownership could be checked.object
403The caller holds a seat in this conversation but is not its owner.object
404No conversation this caller holds a seat in answers to that id. Never distinguished from one that does not exist.object
429Too many requestsobject

Example

Open or close a conversation. Its owner is the only caller who may., with a token from the door.

PATCH /_/api/v1/conversations/{id}
curl -X PATCH 'https://zpe.vc/chat/_/api/v1/conversations/string' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "state": "open"
}'
200 application/json
{
  "conversation": {}
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /_/api/v1/conversations/{id}/messages Post a message into a conversation, under a number this conversation has not used. postConversationsByIdMessages

Post a message into a conversation, under a number this conversation has not used.

Parameters

nameinrequiredtypenotes
idpathyesstringthe conversation to post into, as `conversation.id` spells it
Idempotency-Keyheadernostringthe caller's own retry handle. The same key posted twice writes one row and answers the message id the first call answered.

Request body application/json · required

fieldtyperequirednotes
bodystringyes
source"app" | "upwork" | "email"noWhere the message came from. Defaults to `app`. A session may not set it: only a credential can say a message arrived from somewhere other than this app.
source_idstring | nullnoThe id in that system, for a message that arrived from one.

Responses

statusmeaningbody
200That `Idempotency-Key` was already here: nothing was written, no number was spent, and the row is the one the first call answered with.object
201A message this call wrote: its `seq` is the conversation's next number, and its `sender_principal` is the session's principal — never a value from the body.object
400The body is not one JSON object, carries no `body`, or the `Idempotency-Key` header is empty.object
401No session and no key, so there is nobody to sign the message.object
403The caller is signed in and is not a member of this conversation.object
404No conversation in the caller's account holds that id. Never distinguished from one that does not exist.object
409The conversation is `closed`. Its owner can reopen it with `PATCH`; nobody can post into it.object
413The body is past the 4,000-character ceiling: `too-long`.object
429Too many requestsobject

Example

Post a message into a conversation, under a number this conversation has not used., with a token from the door.

POST /_/api/v1/conversations/{id}/messages
curl -X POST 'https://zpe.vc/chat/_/api/v1/conversations/string/messages' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "body": "string",
  "source": "app",
  "source_id": null
}'
200 application/json
{
  "message": {}
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /_/api/v1/conversations/{id}/members Add a principal to a conversation. A `direct` conversation takes exactly one other person. postConversationsByIdMembers

Add a principal to a conversation. A `direct` conversation takes exactly one other person.

Parameters

nameinrequiredtypenotes
idpathyesstringthe conversation the seat is being written into
Idempotency-Keyheadernostringthe caller's own retry handle. The same key twice answers the seat the first call wrote, and a key this conversation already holds is never spent on a second person.

Request body application/json · required

fieldtyperequirednotes
principalstringyesA principal the door names.
role"owner" | "member"no

Responses

statusmeaningbody
200Nothing new was written: either that principal already held a seat here, or this `Idempotency-Key` already names a seat — and then the answer is that seat, whichever principal the body names. A seat that had `left_at` set is live again.object
201A seat this call created: body `{"principal_id": "…"}`. The principal is now a member of this conversation's account, and may read and post in it.object
400The body names no `principal_id`, the conversation is `direct` and already has both its people, or the `Idempotency-Key` header is empty.object
401No session and no key, so there is nobody to add.object
403This account may not do thatobject
404No conversation this caller holds a seat in answers to that id. Never distinguished from one that does not exist.object
429Too many requestsobject

Example

Add a principal to a conversation. A `direct` conversation takes exactly one other person., with a token from the door.

POST /_/api/v1/conversations/{id}/members
curl -X POST 'https://zpe.vc/chat/_/api/v1/conversations/string/members' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "principal": "string",
  "role": "owner"
}'
200 application/json
{
  "member": {}
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /_/api/v1/conversations/{id}/read Move this caller's read watermark forward to a message number. postConversationsByIdRead

Move this caller's read watermark forward to a message number.

Parameters

nameinrequiredtypenotes
idpathyesstringthe conversation whose watermark is being moved

Request body application/json · required

fieldtyperequirednotes
seqintegeryes

Responses

statusmeaningbody
200The watermark this caller now holds, read back from the row. A `seq` below it leaves it unchanged: the answer is the value storage holds, never the value that was sent.object
400The body is not `{seq}`, `seq` is not a whole number, or `seq` is past the last number this conversation has handed out.object
401No session and no key, so there is nobody whose watermark could move.object
403This account may not do thatobject
404No conversation this caller holds a seat in answers to that id. Never distinguished from one that does not exist.object
429Too many requestsobject

Example

Move this caller's read watermark forward to a message number., with a token from the door.

POST /_/api/v1/conversations/{id}/read
curl -X POST 'https://zpe.vc/chat/_/api/v1/conversations/string/read' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "seq": 0
}'
200 application/json
{
  "read_seq": 0,
  "unread": 0
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

DELETE /_/api/v1/conversations/{id}/messages/{message_id} Delete one message. Its sender or the conversation's owner may. deleteConversationsByIdMessagesById

Delete one message. Its sender or the conversation's owner may.

Parameters

nameinrequiredtypenotes
idpathyesstringthe conversation the message is in, as `conversation.id` spells it
message_idpathyesstringthe message to remove, as `message.id` spells it

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
204The row is gone. A deletion here is real: nothing marks the gap, `seq` is not reused, and "this was said" cannot be shown afterwards.
400The request could not be readobject
401No session and no key, so there is nobody whose authority could be checked.object
403The caller holds a seat here but is neither the conversation's owner nor the message's sender.object
404No conversation this caller holds a seat in answers to that id, or no such message is in it. The two are never distinguished.object
429Too many requestsobject

Example

Delete one message. Its sender or the conversation's owner may., with a token from the door.

DELETE /_/api/v1/conversations/{id}/messages/{message_id}
curl -X DELETE 'https://zpe.vc/chat/_/api/v1/conversations/string/messages/string' \
  -H 'Authorization: Bearer $ZPE_TOKEN'

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.