Chat API Reference

Complete API documentation for the Phenom Chat system, covering Hasura Lite GraphQL, MCP tools, and Matrix/Synapse endpoints.

Hasura Lite GraphQL API

Base endpoint: https://chat-testing.thephenom.app/api/graphql

Authentication: Authorization: Bearer <Cognito ID Token> header on all requests. The token must contain x-hasura-* claims.

Queries

chat_rooms – List Rooms

query ListRooms {
  chat_rooms(where: { is_active: { _eq: true } }) {
    id
    name
    description
    created_at
    is_active
  }
}

Response:

{
  "data": {
    "chat_rooms": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440001",
        "name": "Phenom Internal",
        "description": "Internal team chat",
        "created_at": "2026-03-15T00:00:00+00:00",
        "is_active": true
      },
      {
        "id": "550e8400-e29b-41d4-a716-446655440002",
        "name": "Phenom Partners",
        "description": "Partner and collaborator chat",
        "created_at": "2026-03-15T00:00:00+00:00",
        "is_active": true
      },
      {
        "id": "550e8400-e29b-41d4-a716-446655440003",
        "name": "Phenom Community",
        "description": "Community chat for mobile app users",
        "created_at": "2026-03-15T00:00:00+00:00",
        "is_active": true
      }
    ]
  }
}

chat_messages – Query Messages

Fetch messages with pagination and filtering. Messages are ordered newest-first by default.

query GetMessages(
  $roomId: uuid!
  $limit: Int!
  $before: timestamptz
) {
  chat_messages(
    where: {
      room_id: { _eq: $roomId }
      is_deleted: { _eq: false }
      created_at: { _lt: $before }
    }
    order_by: { created_at: desc }
    limit: $limit
  ) {
    id
    room_id
    user_id
    content
    message_type
    phenom_id
    is_deleted
    created_at
    updated_at
    user {
      id
      username
      display_name
      avatar_url
    }
    link_preview {
      url
      title
      description
      thumbnail_url
      media_type
    }
  }
}

Variables:

{
  "roomId": "550e8400-e29b-41d4-a716-446655440003",
  "limit": 50,
  "before": "2026-03-22T12:00:00Z"
}

Filter messages by a specific user:

query MessagesByUser($roomId: uuid!, $userId: String!, $limit: Int!) {
  chat_messages(
    where: {
      room_id: { _eq: $roomId }
      user_id: { _eq: $userId }
      is_deleted: { _eq: false }
    }
    order_by: { created_at: desc }
    limit: $limit
  ) {
    id
    content
    created_at
    user { username }
  }
}

chat_members – Query Members

query GetMembers($roomId: uuid!) {
  chat_members(
    where: { room_id: { _eq: $roomId } }
    order_by: { joined_at: asc }
  ) {
    user_id
    role
    is_muted
    muted_until
    joined_at
    user {
      username
      display_name
      avatar_url
    }
  }
}

Mutations

insert_chat_messages_one – Send Message

mutation SendMessage($roomId: uuid!, $content: String!) {
  insert_chat_messages_one(
    object: {
      room_id: $roomId
      content: $content
    }
  ) {
    id
    content
    message_type
    created_at
    user {
      id
      username
    }
  }
}

Variables:

{
  "roomId": "550e8400-e29b-41d4-a716-446655440003",
  "content": "Hello from the Phenom community!"
}

Send a Phenom link message (triggers link preview resolution):

mutation SendPhenomLink($roomId: uuid!, $content: String!, $phenomId: String!) {
  insert_chat_messages_one(
    object: {
      room_id: $roomId
      content: $content
      message_type: "phenom_link"
      phenom_id: $phenomId
    }
  ) {
    id
    content
    message_type
    phenom_id
    created_at
  }
}

update_chat_messages_by_pk – Soft Delete Message

Requires support or admin role.

mutation DeleteMessage($messageId: uuid!, $deletedBy: String!) {
  update_chat_messages_by_pk(
    pk_columns: { id: $messageId }
    _set: {
      is_deleted: true
      deleted_by: $deletedBy
    }
  ) {
    id
    is_deleted
  }
}

Variables:

{
  "messageId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "deletedBy": "admin-user-uuid"
}

insert_chat_bans_one – Ban User

Requires admin role.

mutation BanUser(
  $roomId: uuid!
  $userId: String!
  $bannedBy: String!
  $reason: String
  $expiresAt: timestamptz
) {
  insert_chat_bans_one(
    object: {
      room_id: $roomId
      user_id: $userId
      banned_by: $bannedBy
      reason: $reason
      expires_at: $expiresAt
    }
  ) {
    id
    user_id
    reason
    banned_at
    expires_at
  }
}

Variables (temporary ban – 24 hours):

{
  "roomId": "550e8400-e29b-41d4-a716-446655440003",
  "userId": "offending-user-id",
  "bannedBy": "admin-user-uuid",
  "reason": "Violation of community guidelines",
  "expiresAt": "2026-03-23T12:00:00Z"
}

update_chat_members – Mute User / Change Role

Mute a user (requires support or admin role):

mutation MuteUser($roomId: uuid!, $userId: String!, $mutedUntil: timestamptz!) {
  update_chat_members(
    where: {
      room_id: { _eq: $roomId }
      user_id: { _eq: $userId }
    }
    _set: {
      is_muted: true
      muted_until: $mutedUntil
    }
  ) {
    affected_rows
  }
}

Change a user’s role (requires admin role):

mutation SetRole($roomId: uuid!, $userId: String!, $role: String!) {
  update_chat_members(
    where: {
      room_id: { _eq: $roomId }
      user_id: { _eq: $userId }
    }
    _set: { role: $role }
  ) {
    affected_rows
  }
}

Valid roles: user, support, admin.

delete_chat_bans – Unban User

Requires admin role.

mutation UnbanUser($roomId: uuid!, $userId: String!) {
  delete_chat_bans(
    where: {
      room_id: { _eq: $roomId }
      user_id: { _eq: $userId }
    }
  ) {
    affected_rows
  }
}

Subscriptions

chat_messages – Real-Time Messages

Subscribe to new messages in a room. Uses graphql-ws protocol over WSS.

subscription OnNewMessages($roomId: uuid!, $since: timestamptz!) {
  chat_messages(
    where: {
      room_id: { _eq: $roomId }
      is_deleted: { _eq: false }
      created_at: { _gt: $since }
    }
    order_by: { created_at: asc }
  ) {
    id
    content
    message_type
    phenom_id
    created_at
    user {
      id
      username
      display_name
      avatar_url
    }
    link_preview {
      url
      title
      thumbnail_url
    }
  }
}

Connection URL: wss://chat-testing.thephenom.app/api/graphql

Connection parameters:

{
  "headers": {
    "Authorization": "Bearer <cognito-id-token>"
  }
}

Hasura Permission Matrix

TableRoleSELECTINSERTUPDATEDELETE
chat_roomsuserAll active rooms
chat_roomsadminAll roomsis_active
chat_messagesuserNon-deleted in joined roomsOwn messages
chat_messagessupportAll in joined roomsOwn messagesis_deleted, deleted_by
chat_messagesadminAllAllAllAll
chat_membersuserOwn membership
chat_memberssupportAll in roomis_muted, muted_until
chat_membersadminAllAllAllAll
chat_bansuser
chat_banssupportAll in room
chat_bansadminAllAllAll
link_previewsuserAll
link_previewsadminAllAllAllAll

MCP Tool Interface

Endpoint: https://chat-testing.thephenom.app/mcp Protocol: MCP over StreamableHTTPServerTransport (POST requests) Authentication: Cognito agent credentials (see Admin Ops for provisioning)

The MCP server exposes 9 tools. The BACKEND_TYPE environment variable determines whether tools delegate to Hasura or Synapse.

1. chat_read_messages

Read recent messages from a room with optional pagination.

{
  "name": "chat_read_messages",
  "description": "Read recent chat messages from a room with optional pagination",
  "inputSchema": {
    "type": "object",
    "properties": {
      "room_id": {
        "type": "string",
        "description": "The room ID to read messages from"
      },
      "limit": {
        "type": "number",
        "description": "Maximum number of messages to return (default 50)"
      },
      "before": {
        "type": "string",
        "description": "Pagination cursor — return messages created before this ISO timestamp or token"
      }
    },
    "required": ["room_id"]
  }
}

2. chat_post_message

Post a message as the AI agent.

{
  "name": "chat_post_message",
  "description": "Post a message to a chat room as the AI agent",
  "inputSchema": {
    "type": "object",
    "properties": {
      "room_id": {
        "type": "string",
        "description": "The room ID to post the message to"
      },
      "content": {
        "type": "string",
        "description": "The message content to post"
      }
    },
    "required": ["room_id", "content"]
  }
}

3. chat_delete_message

Soft-delete a message (moderation action).

{
  "name": "chat_delete_message",
  "description": "Soft-delete a chat message (moderation action)",
  "inputSchema": {
    "type": "object",
    "properties": {
      "message_id": {
        "type": "string",
        "description": "The ID of the message to delete"
      }
    },
    "required": ["message_id"]
  }
}

4. chat_ban_user

Ban a user from a room with optional duration.

{
  "name": "chat_ban_user",
  "description": "Ban a user from the chat room with an optional duration",
  "inputSchema": {
    "type": "object",
    "properties": {
      "room_id": {
        "type": "string",
        "description": "The room ID"
      },
      "user_id": {
        "type": "string",
        "description": "The user ID to ban"
      },
      "reason": {
        "type": "string",
        "description": "Reason for the ban"
      },
      "duration_minutes": {
        "type": "number",
        "description": "Ban duration in minutes (omit for permanent)"
      }
    },
    "required": ["room_id", "user_id"]
  }
}

5. chat_unban_user

Remove a ban from a user.

{
  "name": "chat_unban_user",
  "description": "Remove a ban from a user",
  "inputSchema": {
    "type": "object",
    "properties": {
      "room_id": {
        "type": "string",
        "description": "The room ID"
      },
      "user_id": {
        "type": "string",
        "description": "The user ID to unban"
      }
    },
    "required": ["room_id", "user_id"]
  }
}

6. chat_mute_user

Mute a user for a specified duration.

{
  "name": "chat_mute_user",
  "description": "Mute a user in the chat room for a specified duration",
  "inputSchema": {
    "type": "object",
    "properties": {
      "room_id": {
        "type": "string",
        "description": "The room ID"
      },
      "user_id": {
        "type": "string",
        "description": "The user ID to mute"
      },
      "duration_minutes": {
        "type": "number",
        "description": "Mute duration in minutes (default 30)"
      }
    },
    "required": ["room_id", "user_id"]
  }
}

7. chat_list_members

List room members with optional role filter.

{
  "name": "chat_list_members",
  "description": "List members of a chat room with optional role filter",
  "inputSchema": {
    "type": "object",
    "properties": {
      "room_id": {
        "type": "string",
        "description": "The room ID"
      },
      "role": {
        "type": "string",
        "description": "Filter by role (e.g. 'admin', 'member', 'joined', 'banned')"
      }
    },
    "required": ["room_id"]
  }
}

8. chat_search_messages

Search messages by keyword (case-insensitive ILIKE on Hasura, full-text on Synapse).

{
  "name": "chat_search_messages",
  "description": "Search chat messages by keyword",
  "inputSchema": {
    "type": "object",
    "properties": {
      "room_id": {
        "type": "string",
        "description": "The room ID to search in"
      },
      "query": {
        "type": "string",
        "description": "The search query / keyword"
      },
      "limit": {
        "type": "number",
        "description": "Maximum number of results (default 20)"
      }
    },
    "required": ["room_id", "query"]
  }
}

9. chat_get_room_state

Get room metadata and current state.

{
  "name": "chat_get_room_state",
  "description": "Get metadata and current state of a chat room",
  "inputSchema": {
    "type": "object",
    "properties": {
      "room_id": {
        "type": "string",
        "description": "The room ID"
      }
    },
    "required": ["room_id"]
  }
}

Matrix/Synapse API

Base URL: https://chat-testing.thephenom.app

Authentication: Authorization: Bearer <access_token> header. Obtain the access token via SSO login (see Mobile Integration).

Key Client-Server Endpoints

Login (SSO Redirect)

GET /_matrix/client/v3/login/sso/redirect/cognito?redirectUrl=phenomapp://chat/sso-callback

Redirects the user to Cognito for authentication, then back to the app with a loginToken.

Login (Token Exchange)

POST /_matrix/client/v3/login
Content-Type: application/json

{
  "type": "m.login.token",
  "token": "<loginToken from SSO redirect>"
}

Response:

{
  "access_token": "syt_...",
  "user_id": "@username:chat-testing.thephenom.app",
  "device_id": "ABCDEFGHIJ",
  "home_server": "chat-testing.thephenom.app"
}

Read Messages

GET /_matrix/client/v3/rooms/{roomId}/messages?dir=b&limit=50&filter={"types":["m.room.message"]}
Authorization: Bearer <access_token>

Response:

{
  "chunk": [
    {
      "event_id": "$abc123...",
      "type": "m.room.message",
      "sender": "@user:chat-testing.thephenom.app",
      "origin_server_ts": 1711100000000,
      "content": {
        "msgtype": "m.text",
        "body": "Hello from the chat!"
      }
    }
  ],
  "start": "t123-456",
  "end": "t789-012"
}

Use the end token as the from parameter in subsequent requests for pagination.

Send Message

PUT /_matrix/client/v3/rooms/{roomId}/send/m.room.message/{txnId}
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "msgtype": "m.text",
  "body": "Hello from the Phenom app!"
}

The {txnId} is a unique client-generated transaction ID (e.g., mcp_1711100000000_abc123).

Response:

{
  "event_id": "$newEventId..."
}

Admin API Endpoints

These require an admin access token.

User Management

GET /_synapse/admin/v2/users/{userId}
Authorization: Bearer <admin_access_token>

Returns user profile, creation date, admin status, and device list.

Auto-Join User to Room

POST /_synapse/admin/v1/join/{roomId}
Authorization: Bearer <admin_access_token>
Content-Type: application/json

{
  "user_id": "@username:chat-testing.thephenom.app"
}

Forces a user to join a room (used by the user provisioner Lambda).

Room Admin

GET /_synapse/admin/v1/rooms
Authorization: Bearer <admin_access_token>

GET /_synapse/admin/v1/rooms/{roomId}/members
Authorization: Bearer <admin_access_token>