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#
| Field | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
source_node_id | UUID | Source entity ID |
source_node_type | string | event, user, platform_user, file |
target_node_id | UUID | Target entity ID |
target_node_type | string | event, user, platform_user, file |
edge_type | string | Type of relationship |
app_id | UUID | Application scope |
created_at | datetime | When created |
Edge Types#
Built-in Edge Types#
| Edge Type | Source | Target | Description |
|---|---|---|---|
sent_by | Event | PlatformUser | Who sent the message |
sent_to | Event | PlatformUser | Who received the message |
attachment | Event | File | File attached to message |
platform_user | User | PlatformUser | User’s platform identity |
reply_to | Event | Event | Reply relationship |
triggered_by | Event | Event | Causal 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
Related Concepts#
- Events - The nodes that edges connect
- Users - User entities in the graph
- Conversations - How edges form conversations