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
flowchart LR
Sender["Identity<br/>(sender)"] -->|sent_by| Event["Event<br/>(message)"]
Event -->|sent_to| Recipient["Identity<br/>(recipient)"]
Event -->|attachment| File["File<br/>(image)"]Edge Model#
| Field | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
source_node_id | UUID | Source entity ID |
source_node_type | string | event, user, identity, file |
target_node_id | UUID | Target entity ID |
target_node_type | string | event, user, identity, 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 | Identity | Who sent the message |
sent_to | Event | Identity | Who received the message |
attachment | Event | File | File attached to message |
identity | User | Identity | User’s platform identity |
reply_to | Event | Event | Reply relationship |
triggered_by | Event | Event | Causal relationship |
participant | Event (conversation) | User | Conversation participant |
in_conversation | Event (message) | Event (conversation) | Links message to its conversation |
Custom Edge Types#
You can create custom edge types for your application. Node types are plain
strings ("event", "user", "identity", "file"):
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#
flowchart TB
E["message.inbound<br/>(Event)"] -->|sent_by| U["Identity<br/>(user)"]
E -->|sent_to| S["Identity<br/>(system)"]Outbound Message#
flowchart TB
E["message.outbound<br/>(Event)"] -->|sent_by| S["Identity<br/>(system)"]
E -->|sent_to| U["Identity<br/>(user)"]Querying Edges#
An Edge exposes only its endpoints and type. There are no nested object
resolvers. To get the entity an edge points at, read targetNodeType +
targetNodeId and look the entity up in a second query.
| Field | Description |
|---|---|
edgeType | Relationship type (sent_by, attachment, …) |
sourceNodeType / sourceNodeId | The source entity |
targetNodeType / targetNodeId | The target entity |
extraData | Optional JSON metadata |
Get an Event with its Edges#
query {
event(id: "evt_abc123") {
id
eventType
payload
edges {
edgeType
targetNodeType
targetNodeId
}
}
}To resolve, say, the sender identity from a sent_by edge, take its
targetNodeId and query identities(identityIds:).
Find Events by Edge#
The events query filters directly on edges with relatedNodeId (+ optional
relatedNodeType / relatedEdgeType). Find all messages sent by a specific
identity:
query {
events(relatedNodeId: "id_abc123", relatedEdgeType: "sent_by", first: 50) {
edges {
node {
id
eventType
payload
createdAt
}
}
pageInfo { hasNextPage endCursor }
}
}Alternatively, the root edges query returns the raw edges (a plain list) for
a given filter:
query {
edges(edgeType: "sent_by", targetNodeId: "id_abc123", limit: 50) {
sourceNodeId # the event ID
targetNodeType
targetNodeId
}
}File Attachments#
Files are connected to events via attachment edges. Read the edge, then the
file:
query {
event(id: "evt_abc123") {
id
payload
edges {
edgeType # filter for "attachment" in your client
targetNodeType # "file"
targetNodeId # look up with files(fileIds:)
}
}
}Fetch the files themselves with the attachment edges’ target IDs:
query {
files(fileIds: ["file_xyz789"]) {
id
filename
mimeType
sizeBytes
url
}
}Creating Attachments#
When sending a message with a file, Outeract creates the attachment edge for
you. Read it back from the returned event’s edges:
mutation {
sendMessage(
platformConnectionId: "pc_abc123"
recipientUserId: "user-uuid"
message: "Check out this image"
fileIds: ["file_xyz789"]
) {
id
edges {
edgeType # "attachment"
targetNodeType # "file"
targetNodeId
}
}
}Reply Chains#
Track reply relationships between messages:
flowchart TB
E3["Reply to Reply<br/>evt_003"] -->|reply_to| E2["Reply Message<br/>evt_002"]
E2 -->|reply_to| E1["Original Message<br/>evt_001"]Read an event’s reply_to edge to find its parent, then follow the chain by
querying each parent in turn with event(id:):
query {
event(id: "evt_003") {
id
payload
edges {
edgeType # look for "reply_to"
targetNodeType # "event"
targetNodeId # the parent event ID; query event(id:) again to walk up
}
}
}Creating Custom Edges#
Link events with custom relationships. Node types are strings:
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#
Read the broadcast event’s sent_to edges, then resolve the identities:
query {
edges(sourceNodeId: "evt_broadcast", edgeType: "sent_to") {
targetNodeId # each recipient identity; resolve with identities(identityIds:)
}
}Fan-In: All messages from a user#
The events query filters on the edge directly, so no manual join is needed:
query MessagesFromIdentity($identityId: UUID!) {
events(relatedNodeId: $identityId, relatedEdgeType: "sent_by", last: 50) {
edges {
node {
id
eventType
payload
createdAt
}
}
}
}Raw edges for a specific entity#
When you want the edges themselves rather than the events, use the root edges
query (returns a plain list):
query EdgesForIdentity($identityId: UUID!) {
edges(edgeType: "sent_by", targetNodeId: $identityId, limit: 50) {
sourceNodeId # the message event
targetNodeId # the identity
extraData
}
}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