Events#

Events are the core data model in Outeract. Every message, status update, and action is represented as an immutable event, providing a complete audit trail of all activity.

Overview#

Outeract uses an event-sourced architecture where:

  • All state changes are captured as events
  • Events are immutable (never modified after creation)
  • Current state is derived from the event stream
  • Full history is always available
flowchart LR
    subgraph Stream["Event Stream"]
        E1["message.inbound<br/>t=0"] --> E2["message.outbound<br/>t=1"]
        E2 --> E3["message.delivered<br/>t=2"]
        E3 --> E4["message.read<br/>t=3"]
        E4 --> More["..."]
    end

Event Model#

FieldTypeDescription
idUUIDUnique identifier
app_idUUIDApplication this event belongs to
event_type_idUUIDReference to EventSchema
statusstringpending, processing, completed, failed
payloadJSONEvent-specific data
origin_event_idUUIDParent event (for status updates)
processed_atdatetimeWhen the event was processed
created_atdatetimeWhen the event was created

Event Types#

Events are categorized by type. Built-in types follow a hierarchical naming convention:

Message Events#

message.inbound    - Incoming message from a user
message.outbound   - Outgoing message to a user

Payload Structure:

{
  "type": "message",
  "message": {
    "text": "Hello, world!",
    "role": "user"
  },
  "platform": "whatsapp",
  "external_message_id": "wamid.xxx",
  "created_at": "2024-01-15T10:30:00Z",
  "sent_at": "2024-01-15T10:30:01Z",
  "delivered_at": "2024-01-15T10:30:02Z",
  "read_at": null,
  "failed_at": null
}
link_code.generated   - A link code was created
link_code.activation  - A link code was used

Generation Payload:

{
  "code": "1234-5678-9012-3456",
  "expiry_minutes": 15,
  "max_uses": 1,
  "uses": 0,
  "generated_by_platform_user_id": "pu_abc123"
}

User Events#

user.merged  - Two user records were merged

Custom Events#

You can define custom event types using any valid name. Event type names must:

  • Use lowercase letters, numbers, dots, and underscores only
  • Match the pattern: ^[a-z0-9_.]+$
  • Not use reserved prefixes: message.*, user.*, system.*, link_code.*
order.created
payment.completed
support.ticket.opened
purchase
subscription.renewed

Event Relationships#

Events are connected to other entities via edges:

flowchart TB
    Event["Event<br/>(message.inbound)"]
    Event --> SB["sent_by edge"]
    Event --> ST["sent_to edge"]
    Event --> AT["attachment edge"]
    SB --> PU["Platform User"]
    ST --> SU["System User"]
    AT --> F["File"]

Querying Events#

Get Recent Events#

query {
  events(first: 20) {
    edges {
      node {
        id
        eventType
        payload
        status
        createdAt
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Filter by Event Type#

query {
  events(
    filters: {
      eventType: { contains: "message" }
    }
    first: 50
  ) {
    edges {
      node {
        id
        eventType
        payload
      }
    }
  }
}

Filter by Date Range#

query {
  events(
    filters: {
      createdAt: {
        gte: "2024-01-01T00:00:00Z"
        lte: "2024-01-31T23:59:59Z"
      }
    }
  ) {
    edges {
      node {
        id
        eventType
        createdAt
      }
    }
  }
}

Get Events with Edges#

query {
  events(
    filters: { eventType: { equals: "message.inbound" } }
    first: 10
  ) {
    edges {
      node {
        id
        eventType
        payload
        edges {
          edgeType
          targetPlatformUser {
            id
            externalId
          }
        }
      }
    }
  }
}

Creating Events#

Via Message API#

When you send a message, an event is automatically created:

mutation {
  sendMessage(
    platformConnectionId: "pc_abc123"
    toExternalId: "+14155551234"
    message: "Hello!"
  ) {
    id
    status {
      success
    }
  }
}

This creates a message.outbound event with appropriate edges.

Custom Events#

Create custom events for your application:

mutation {
  createEvent(
    eventType: "order.created"
    payload: {
      order_id: "order_12345"
      total: 99.99
      currency: "USD"
    }
  ) {
    id
    eventType
    payload
  }
}

Event Status#

Events have a lifecycle status:

StatusDescription
pendingEvent created, not yet processed
processingCurrently being processed
completedSuccessfully processed
failedProcessing failed

Status Updates#

Status updates create child events linked via origin_event_id:

flowchart TB
    Parent["message.outbound<br/>id: evt_001<br/>status: completed"]
    Parent -->|origin_event_id| Delivered["delivered status<br/>evt_002"]
    Parent -->|origin_event_id| Read["read status<br/>evt_003"]
    Parent -->|origin_event_id| Failed["failed status"]

Query status updates:

query {
  event(id: "evt_001") {
    id
    eventType
    childEvents {
      id
      eventType
      payload
    }
  }
}

Event Schemas#

Event types are defined by EventSchemas:

query {
  eventSchemas {
    id
    name
    jsonSchema
    enforceValidation
    isGlobal
  }
}

Built-in (Global) Schemas#

Global schemas are available to all applications:

  • message.inbound
  • message.outbound
  • link_code.generated
  • link_code.activation

Custom Schemas#

Define custom event types with JSON Schema validation:

mutation {
  createEventSchema(
    name: "order.created"
    jsonSchema: {
      type: "object"
      required: ["order_id", "total"]
      properties: {
        order_id: { type: "string" }
        total: { type: "number", minimum: 0 }
        currency: { type: "string", enum: ["USD", "EUR", "GBP"] }
      }
    }
    enforceValidation: true
  ) {
    id
    name
  }
}

Event Subscriptions (Webhooks)#

Subscribe to events via outbound webhooks:

mutation {
  createWebhookSubscription(
    name: "My Webhook"
    targetUrl: "https://myapp.com/webhooks/outeract"
    eventPatterns: ["message.inbound", "order.*"]
    secret: "my-webhook-secret"
  ) {
    id
    name
  }
}

When matching events occur, Outeract POSTs to your URL:

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

Learn more about Webhooks →

Best Practices#

1. Use Event Types Consistently#

Follow the category.action naming convention for custom events.

2. Include Relevant Data in Payloads#

Store enough context in the payload to understand the event without external lookups.

3. Don’t Store Sensitive Data#

Avoid putting passwords, tokens, or PII in event payloads.

4. Use Edges for Relationships#

Link events to users and files via edges, not payload fields.

5. Handle Idempotency#

Events may be delivered multiple times. Use external_message_id for deduplication.