Conversations#

Conversations in Outeract are logical groupings of messages between participants. They emerge naturally from the event graph through edges, providing context for message threads and enabling conversation-level analytics.

Overview#

Unlike traditional chat systems with explicit conversation objects, Outeract derives conversations from message relationships:

flowchart TB
    subgraph Conv["Conversation View<br/>User: Alice (+14155551234) • Platform: WhatsApp"]
        M1["[10:30] Alice: Hi, I need help with my order"]
        M2["[10:31] Bot: Hello! I'd be happy to help..."]
        M3["[10:32] Alice: Order #12345"]
        M4["[10:33] Bot: I found your order. It's shipping..."]
        M5["[10:35] Alice: Thanks!"]
        M1 --> M2 --> M3 --> M4 --> M5
    end

Conversation Model#

Conversations are virtual aggregations built from:

ComponentRole
EventsIndividual messages (inbound/outbound)
Edgessent_by and sent_to relationships
Platform UsersConversation participants
Platform ConnectionThe channel (WhatsApp, Slack, etc.)

Identifying Conversations#

By Platform User#

The most common pattern - get all messages to/from a specific user:

query ConversationWithUser($platformUserId: UUID!) {
  events(
    filters: {
      eventType: { contains: "message" }
      or: [
        { hasEdge: { edgeType: "sent_by", targetNodeId: { equals: $platformUserId } } }
        { hasEdge: { edgeType: "sent_to", targetNodeId: { equals: $platformUserId } } }
      ]
    }
    orderBy: { field: CREATED_AT, direction: ASC }
  ) {
    edges {
      node {
        id
        eventType
        payload
        createdAt
        edges {
          edgeType
          targetPlatformUser {
            id
            externalId
          }
        }
      }
    }
  }
}

By User (Cross-Platform)#

Get conversations across all platforms for a unified user:

query CrossPlatformConversation($userId: UUID!) {
  user(id: $userId) {
    id
    name
    platformUsers {
      id
      externalId
      platformConnection {
        platformName
      }
      # Get messages for each platform identity
      sentMessages: events(
        filters: { hasEdge: { edgeType: "sent_by" } }
        first: 50
      ) {
        edges {
          node {
            id
            payload
            createdAt
          }
        }
      }
    }
  }
}

By Time Window#

Get recent conversations with activity:

query RecentConversations($since: DateTime!) {
  events(
    filters: {
      eventType: { equals: "message.inbound" }
      createdAt: { gte: $since }
    }
    orderBy: { field: CREATED_AT, direction: DESC }
  ) {
    edges {
      node {
        id
        payload
        createdAt
        edges(edgeType: "sent_by") {
          targetPlatformUser {
            id
            externalId
            name
            user {
              id
              name
            }
          }
        }
      }
    }
  }
}

Conversation Patterns#

1:1 Conversations#

Direct messages between your system and a user:

flowchart LR
    PU["Platform User<br/>(Customer)"] <-->|messages| SU["System User<br/>(Your Bot)"]

Query pattern:

query DirectConversation($platformUserId: UUID!, $systemUserId: UUID!) {
  events(
    filters: {
      eventType: { contains: "message" }
      and: [
        {
          or: [
            { hasEdge: { edgeType: "sent_by", targetNodeId: { equals: $platformUserId } } }
            { hasEdge: { edgeType: "sent_by", targetNodeId: { equals: $systemUserId } } }
          ]
        }
        {
          or: [
            { hasEdge: { edgeType: "sent_to", targetNodeId: { equals: $platformUserId } } }
            { hasEdge: { edgeType: "sent_to", targetNodeId: { equals: $systemUserId } } }
          ]
        }
      ]
    }
    orderBy: { field: CREATED_AT, direction: ASC }
  ) {
    edges {
      node {
        id
        eventType
        payload
        createdAt
      }
    }
  }
}

Group Conversations#

Messages with multiple recipients (e.g., Slack channels):

flowchart TB
    GM["Group Message<br/>(Event)"]
    GM --> A["sent_to<br/>User A"]
    GM --> B["sent_to<br/>User B"]
    GM --> C["sent_to<br/>User C"]

Threaded Conversations#

Reply chains using reply_to edges:

query ThreadedConversation($rootMessageId: UUID!) {
  event(id: $rootMessageId) {
    id
    payload
    # Get all replies
    replies: incomingEdges(edgeType: "reply_to") {
      sourceEvent {
        id
        payload
        createdAt
        edges(edgeType: "sent_by") {
          targetPlatformUser {
            name
          }
        }
        # Nested replies
        replies: incomingEdges(edgeType: "reply_to") {
          sourceEvent {
            id
            payload
          }
        }
      }
    }
  }
}

Building a Conversation List#

Get Active Conversations#

List users with recent message activity:

query ActiveConversations($since: DateTime!) {
  platformUsers(
    filters: {
      hasEvents: {
        eventType: { contains: "message" }
        createdAt: { gte: $since }
      }
    }
    first: 50
  ) {
    edges {
      node {
        id
        externalId
        name
        platformConnection {
          platformName
        }
        # Latest message
        latestMessage: events(
          filters: { eventType: { contains: "message" } }
          orderBy: { field: CREATED_AT, direction: DESC }
          first: 1
        ) {
          edges {
            node {
              id
              eventType
              payload
              createdAt
            }
          }
        }
        # Unread count (messages since last outbound)
        messageCount: eventsCount(
          filters: { eventType: { equals: "message.inbound" } }
        )
      }
    }
  }
}

Conversation Summary#

Get conversation metadata:

query ConversationSummary($platformUserId: UUID!) {
  platformUser(id: $platformUserId) {
    id
    externalId
    name

    # First message
    firstMessage: events(
      filters: { eventType: { contains: "message" } }
      orderBy: { field: CREATED_AT, direction: ASC }
      first: 1
    ) {
      edges {
        node {
          createdAt
        }
      }
    }

    # Latest message
    latestMessage: events(
      filters: { eventType: { contains: "message" } }
      orderBy: { field: CREATED_AT, direction: DESC }
      first: 1
    ) {
      edges {
        node {
          createdAt
          payload
        }
      }
    }

    # Message counts
    inboundCount: eventsCount(
      filters: { eventType: { equals: "message.inbound" } }
    )
    outboundCount: eventsCount(
      filters: { eventType: { equals: "message.outbound" } }
    )
  }
}

Message Context#

Get Surrounding Messages#

Fetch context around a specific message:

query MessageContext($messageId: UUID!, $platformUserId: UUID!) {
  # Messages before
  before: events(
    filters: {
      eventType: { contains: "message" }
      hasEdge: { targetNodeId: { equals: $platformUserId } }
      id: { lt: $messageId }
    }
    orderBy: { field: CREATED_AT, direction: DESC }
    first: 5
  ) {
    edges {
      node {
        id
        payload
        createdAt
      }
    }
  }

  # The message itself
  message: event(id: $messageId) {
    id
    payload
    createdAt
  }

  # Messages after
  after: events(
    filters: {
      eventType: { contains: "message" }
      hasEdge: { targetNodeId: { equals: $platformUserId } }
      id: { gt: $messageId }
    }
    orderBy: { field: CREATED_AT, direction: ASC }
    first: 5
  ) {
    edges {
      node {
        id
        payload
        createdAt
      }
    }
  }
}

Conversation State#

Track conversation state using custom events:

# Mark conversation as handled
mutation MarkHandled($platformUserId: UUID!) {
  createEvent(
    eventType: "custom.conversation.handled"
    payload: {
      platform_user_id: $platformUserId
      handled_by: "agent_123"
      handled_at: "2024-01-15T10:30:00Z"
    }
  ) {
    id
  }
}

# Query conversation state
query ConversationState($platformUserId: UUID!) {
  events(
    filters: {
      eventType: { equals: "custom.conversation.handled" }
      payload: { path: "platform_user_id", equals: $platformUserId }
    }
    orderBy: { field: CREATED_AT, direction: DESC }
    first: 1
  ) {
    edges {
      node {
        payload
        createdAt
      }
    }
  }
}

Real-Time Updates#

Subscribe to conversation updates via webhooks:

mutation SubscribeToMessages {
  createWebhookSubscription(
    name: "Conversation Updates"
    targetUrl: "https://myapp.com/webhooks/messages"
    eventPatterns: ["message.inbound", "message.outbound"]
    secret: "webhook-secret"
  ) {
    id
  }
}

Your webhook receives:

{
  "event_id": "evt_abc123",
  "event_type": "message.inbound",
  "payload": {
    "message": { "text": "Hello!" }
  },
  "edges": {
    "sent_by": {
      "platform_user_id": "pu_xyz789",
      "external_id": "+14155551234"
    }
  },
  "created_at": "2024-01-15T10:30:00Z"
}

Pagination#

For long conversations, use cursor-based pagination:

query PaginatedConversation($platformUserId: UUID!, $cursor: String) {
  events(
    filters: {
      eventType: { contains: "message" }
      hasEdge: { targetNodeId: { equals: $platformUserId } }
    }
    orderBy: { field: CREATED_AT, direction: DESC }
    first: 20
    after: $cursor
  ) {
    edges {
      node {
        id
        payload
        createdAt
      }
      cursor
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Best Practices#

1. Index by Platform User#

Structure queries around platform users for efficient conversation retrieval.

2. Use Pagination#

Always paginate message queries - conversations can have thousands of messages.

3. Cache Conversation Metadata#

Cache summaries (message count, last activity) and update incrementally.

4. Handle Multiple Platforms#

Remember that one User can have conversations across multiple Platform Users.

5. Track State Externally#

Store conversation state (open/closed, assigned agent) in your application.

6. Use Webhooks for Real-Time#

Don’t poll for new messages - subscribe to webhooks for real-time updates.

  • Events - The messages that form conversations
  • Edges - How messages connect to participants
  • Users - The participants in conversations