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#

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

Edge Types#

Built-in Edge Types#

Edge TypeSourceTargetDescription
sent_byEventIdentityWho sent the message
sent_toEventIdentityWho received the message
attachmentEventFileFile attached to message
identityUserIdentityUser’s platform identity
reply_toEventEventReply relationship
triggered_byEventEventCausal relationship
participantEvent (conversation)UserConversation participant
in_conversationEvent (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.

FieldDescription
edgeTypeRelationship type (sent_by, attachment, …)
sourceNodeType / sourceNodeIdThe source entity
targetNodeType / targetNodeIdThe target entity
extraDataOptional 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