Files
postcard-api/WEBSOCKET_API_SPEC.md

5.5 KiB

WebSocket API Specification v0.1

This document outlines the WebSocket protocol for the chat application. It defines the message envelope and all frame types for client-server communication.

0.1 — Protocol Specification

Goal

Define the shared message envelope and all WebSocket frame types as a single source of truth that every client and the backend implement against.

WebSocket Envelope Format

All messages sent over the WebSocket connection will be JSON objects conforming to the following structure:

{
  "type": "string",
  "payload": "object",
  "id": "string",
  "timestamp": "string"
}
  • type: The type of frame being sent. This determines the shape of the payload object.
  • payload: A JSON object containing the data for the frame.
  • id: A unique identifier for the message (e.g., a UUID).
  • timestamp: An ISO 8601 formatted timestamp string indicating when the message was created.

Client → Server Frame Types

Authenticate

Sent by the client to authenticate the WebSocket session upon connection.

Payload:

{
  "token": "string" // JWT token
}

SendMessage

Sent by the client to send a message to a conversation.

Payload:

{
  "conversation_id": "string",
  "client_message_id": "string", // UUID generated client-side for idempotency
  "type": "string", // "text" | "image" | "audio" | "gif" | "sticker"
  "body": "string", // Nullable
  "media_ref": "string" // Nullable, reference to uploaded media
}

TypingStart / TypingStop

Sent by the client to indicate typing activity.

Payload:

{
  "conversation_id": "string"
}

MarkRead

Sent by the client to mark messages in a conversation as read up to a certain sequence number.

Payload:

{
  "conversation_id": "string",
  "sequence": "number"
}

AckDelivery

Sent by the client to acknowledge the delivery of a message.

Payload:

{
  "conversation_id": "string",
  "sequence": "number"
}

Ping

Sent by the client to check the connection status. The server will respond with a Pong.

Payload:

{}

Sync

Sent by the client upon reconnection to sync missed messages.

Payload:

{
  "conversations": [
    {
      "id": "string",
      "last_sequence": "number"
    }
  ]
}

Server → Client Frame Types

NewMessage

Sent by the server when a new message is added to a conversation.

Payload:

{
  "conversation_id": "string",
  "sequence": "number",
  "sender_id": "string",
  "type": "string", // "text" | "image" | "audio" | "gif" | "sticker"
  "body": "string", // Nullable
  "media": "object", // Nullable Media metadata object
  "timestamp": "string", // ISO 8601
  "client_message_id": "string" // Echoed back from sender
}

MessageDelivered

Sent by the server to confirm a message has been delivered.

Payload:

{
  "conversation_id": "string",
  "sequence": "number",
  "timestamp": "string" // ISO 8601
}

MessageRead

Sent by the server to indicate a message has been read by a recipient.

Payload:

{
  "conversation_id": "string",
  "sequence": "number",
  "timestamp": "string" // ISO 8601
}

TypingIndicator

Sent by the server to show that a user is typing in a conversation.

Payload:

{
  "conversation_id": "string",
  "user_id": "string",
  "active": "boolean"
}

PresenceUpdate

Sent by the server to update a user's presence status.

Payload:

{
  "user_id": "string",
  "status": "string", // "online" | "offline"
  "last_seen": "string" // ISO 8601
}

MediaReady

Sent by the server when media processing is complete and URLs are available.

Payload:

{
  "conversation_id": "string",
  "sequence": "number",
  "media": "object" // Full Media metadata object
}

Error

Sent by the server when an error occurs.

Payload:

{
  "code": "string", // See Error Code Enum
  "message": "string",
  "ref_id": "string" // Optional, correlates to the client request ID
}

Pong

Sent by the server in response to a client's Ping.

Payload:

{}

Media Metadata Object

This object contains metadata for media attachments.

{
  "original_url": "string",
  "thumbnail_url": "string",
  "preview_url": "string",
  "width": "number",
  "height": "number",
  "duration_seconds": "number", // Nullable
  "file_size_bytes": "number",
  "mime_type": "string",
  "blurhash": "string" // Nullable
}

Error Code Enum

  • auth_failed: Authentication failed.
  • rate_limited: The client is sending messages too frequently.
  • invalid_payload: The message payload is malformed or invalid.
  • conversation_not_found: The specified conversation does not exist.
  • internal_server_error: An unexpected error occurred on the server.
  • not_authorized: The client is not authorized to perform the action.

Reconnection Contract

If a client disconnects, it can attempt to reconnect and sync missed messages.

  1. Client reconnects to the WebSocket.
  2. Client sends a Sync message.

Sync Frame (Client → Server):

{
  "type": "Sync",
  "payload": {
    "conversations": [
      {
        "id": "string",
        "last_sequence": "number"
      }
    ]
  },
  "id": "string",
  "timestamp": "string"
}
  1. The server will respond with a batch of NewMessage frames for each conversation, containing all messages since the last_sequence number provided by the client.