Edges#

Edges are the relationships that connect events, users, and files in Outeract’s graph model. They provide full provenance tracking - who sent what, to whom, and what triggered what.

Overview#

Outeract uses a directed graph where:

  • Nodes are events, users, platform users, and files
  • Edges are typed relationships between nodes
┌────────────────────────────────────────────────────────────────┐
│                        Event Graph                              │
│                                                                │
│     ┌───────────┐    sent_by     ┌─────────────────┐          │
│     │   Event   │◄───────────────│  Platform User  │          │
│     │ (message) │                │   (sender)      │          │
│     └─────┬─────┘                └─────────────────┘          │
│           │                                                    │
│           │ sent_to              ┌─────────────────┐          │
│           └─────────────────────►│  Platform User  │          │
│           │                      │   (recipient)   │          │
│           │                      └─────────────────┘          │
│           │ attachment                                         │
│           └─────────────────────►┌───────────────┐            │
│                                  │     File      │            │
│                                  │   (image)     │            │
│                                  └───────────────┘            │
└────────────────────────────────────────────────────────────────┘

Edge Model#

FieldTypeDescription
idUUIDUnique identifier
source_node_idUUIDSource entity ID
source_node_typestringevent, user, platform_user, file
target_node_idUUIDTarget entity ID
target_node_typestringevent, user, platform_user, file
edge_typestringType of relationship
app_idUUIDApplication scope
created_atdatetimeWhen created

Edge Types#

Built-in Edge Types#

Edge TypeSourceTargetDescription
sent_byEventPlatformUserWho sent the message
sent_toEventPlatformUserWho received the message
attachmentEventFileFile attached to message
platform_userUserPlatformUserUser’s platform identity
reply_toEventEventReply relationship
triggered_byEventEventCausal relationship

Custom Edge Types#

You can create custom edge types for your application:

mutation {
  createEdge(
    sourceNodeId: "evt_abc123"
    sourceNodeType: EVENT
    targetNodeId: "evt_xyz789"
    targetNodeType: EVENT
    edgeType: "follow_up"
  ) {
    id
    edgeType
  }
}

Message Edges#

Every message event has edges that track sender and recipient:

Inbound Message#

┌──────────────────────┐
│    message.inbound   │
│      (Event)         │
└──────────┬───────────┘
           │
     ┌─────┴─────┐
     │           │
     ▼           ▼
┌─────────┐  ┌─────────┐
│ sent_by │  │ sent_to │
│ (User)  │  │ (System)│
└─────────┘  └─────────┘

Outbound Message#

┌──────────────────────┐
│   message.outbound   │
│      (Event)         │
└──────────┬───────────┘
           │
     ┌─────┴─────┐
     │           │
     ▼           ▼
┌─────────┐  ┌─────────┐
│ sent_by │  │ sent_to │
│ (System)│  │ (User)  │
└─────────┘  └─────────┘

Querying Edges#

Get Event with Edges#

query {
  event(id: "evt_abc123") {
    id
    eventType
    payload
    edges {
      id
      edgeType
      targetNodeType
      targetUser {
        id
        name
      }
      targetPlatformUser {
        id
        externalId
      }
      targetFile {
        id
        filename
        mimeType
      }
    }
  }
}

Find Events by Edge#

Find all messages sent by a specific user:

query {
  edges(
    filters: {
      edgeType: { equals: "sent_by" }
      targetNodeId: { equals: "pu_abc123" }
    }
  ) {
    edges {
      node {
        sourceEvent {
          id
          eventType
          payload
          createdAt
        }
      }
    }
  }
}

Query Conversation Thread#

Get all messages between two users:

query ConversationThread($user1: UUID!, $user2: UUID!) {
  events(
    filters: {
      eventType: { contains: "message" }
      hasEdge: {
        edgeType: "sent_by"
        targetNodeId: { in: [$user1, $user2] }
      }
    }
    orderBy: { field: CREATED_AT, direction: ASC }
  ) {
    edges {
      node {
        id
        eventType
        payload
        edges {
          edgeType
          targetPlatformUser {
            externalId
          }
        }
      }
    }
  }
}

File Attachments#

Files are connected to events via attachment edges:

query {
  event(id: "evt_abc123") {
    id
    payload
    attachments: edges(edgeType: "attachment") {
      targetFile {
        id
        filename
        mimeType
        sizeBytes
        url
      }
    }
  }
}

Creating Attachments#

When sending a message with a file:

mutation {
  sendMessage(
    platformConnectionId: "pc_abc123"
    toExternalId: "+14155551234"
    message: "Check out this image"
    fileIds: ["file_xyz789"]
  ) {
    id
    edges {
      edgeType
      targetFile {
        id
      }
    }
  }
}

Reply Chains#

Track reply relationships between messages:

┌──────────────────┐
│ Original Message │
│    evt_001       │
└────────┬─────────┘
         │ reply_to
         │
┌────────▼─────────┐
│ Reply Message    │
│    evt_002       │
└────────┬─────────┘
         │ reply_to
         │
┌────────▼─────────┐
│ Reply to Reply   │
│    evt_003       │
└──────────────────┘

Query a thread:

query {
  event(id: "evt_003") {
    id
    # Get parent message
    replyTo: edges(edgeType: "reply_to") {
      targetEvent {
        id
        payload
        # Get grandparent
        replyTo: edges(edgeType: "reply_to") {
          targetEvent {
            id
            payload
          }
        }
      }
    }
  }
}

Creating Custom Edges#

Link events with custom relationships:

mutation {
  createEdge(
    sourceNodeId: "evt_support_ticket"
    sourceNodeType: EVENT
    targetNodeId: "evt_resolution"
    targetNodeType: EVENT
    edgeType: "resolved_by"
  ) {
    id
    edgeType
  }
}

Edge Traversal Patterns#

Fan-Out: All recipients of a broadcast#

query {
  event(id: "evt_broadcast") {
    edges(edgeType: "sent_to") {
      targetPlatformUser {
        id
        externalId
        user {
          name
        }
      }
    }
  }
}

Fan-In: All messages from a user#

query MessagesFromUser($platformUserId: UUID!) {
  edges(
    filters: {
      edgeType: { equals: "sent_by" }
      targetNodeId: { equals: $platformUserId }
      sourceNodeType: { equals: "event" }
    }
    orderBy: { field: CREATED_AT, direction: DESC }
    first: 50
  ) {
    edges {
      node {
        sourceEvent {
          id
          eventType
          payload
          createdAt
        }
      }
    }
  }
}

Bidirectional: Conversation between users#

query Conversation($user1: UUID!, $user2: UUID!) {
  # Messages from user1 to user2
  fromUser1: edges(
    filters: {
      edgeType: { equals: "sent_by" }
      targetNodeId: { equals: $user1 }
    }
  ) {
    edges {
      node {
        sourceEvent {
          id
          edges(edgeType: "sent_to") {
            targetNodeId
          }
        }
      }
    }
  }
}

Best Practices#

1. Use Built-in Edge Types#

Use standard edge types (sent_by, sent_to, attachment) when applicable.

2. Keep Edge Types Consistent#

Define a vocabulary of edge types and use them consistently.

3. Don’t Duplicate Data#

Use edges for relationships instead of embedding IDs in payloads.

4. Consider Query Patterns#

Design edge types based on how you’ll query them.

5. Edge Type Naming#

Use lowercase with underscores: sent_by, reply_to, triggered_by