Event Types#

Complete reference of all built-in event types in Outeract.

Quick Reference#

Event TypeCategoryRequired FieldsDescription
file.inboundfilefile_idBuilt-in schema for file.inbound events
link_code.activationlink_codelink_code_event_idBuilt-in schema for link_code.activation events
link_code.generatedlink_codecodeBuilt-in schema for link_code.generated events
message.inboundmessagetype, message, platformBuilt-in schema for message.inbound events
message.outboundmessagetype, message, platformBuilt-in schema for message.outbound events
user.mergedusersource_user_id, target_user_idBuilt-in schema for user.merged events

Reserved prefixes: file.*, link_code.*, message.*, user.*

Message Events#

message.inbound#

Incoming message from a user.

Payload:

{
  "type": "message",
  "message": {
    "text": "Hello!",
    "role": "user",
    "type": "text"
  },
  "platform": "whatsapp",
  "external_message_id": "wamid.xxx",
  "created_at": "2024-01-15T10:30:00Z"
}

Edges:

  • sent_by → Platform User (sender)
  • sent_to → Platform User (system user)
  • attachment → File (if media attached)

message.outbound#

Outgoing message sent to a user.

Payload:

{
  "type": "message",
  "message": {
    "text": "Thank you for contacting us!",
    "role": "assistant",
    "type": "text"
  },
  "platform": "whatsapp",
  "external_message_id": "wamid.xxx",
  "sent_at": "2024-01-15T10:30:00Z"
}

Edges:

  • sent_by → Platform User (system user)
  • sent_to → Platform User (recipient)
  • attachment → File (if media attached)

message.delivered#

Message was delivered to recipient’s device.

Payload:

{
  "status": "delivered",
  "external_message_id": "wamid.xxx",
  "delivered_at": "2024-01-15T10:30:05Z"
}

Origin Event: Links to message.outbound


message.read#

Message was read by recipient.

Payload:

{
  "status": "read",
  "external_message_id": "wamid.xxx",
  "read_at": "2024-01-15T10:31:00Z"
}

Origin Event: Links to message.outbound


system.message_delivery_failed#

Message delivery failed (e.g., 24-hour messaging window expired).

Payload:

{
  "type": "message_failed",
  "original_event_id": "evt_abc123",
  "external_message_id": "wamid.xxx",
  "platform": "whatsapp",
  "failed_at": "2024-01-15T10:30:10Z",
  "error": "Message failed to send because more than 24 hours have passed since the customer last replied",
  "error_type": "messaging_window_closed",
  "original_text": "Hello, this is a test message"
}

Origin Event: Links to message.outbound

Edges: Copies all edges from the original message (in_conversation, sent_by, sent_to)


message.reaction#

Emoji reaction to a message.

Payload:

{
  "reaction": {
    "emoji": "👍",
    "message_id": "wamid.xxx"
  },
  "platform": "whatsapp"
}

Edges:

  • sent_by → Platform User (reactor)
  • reply_to → Event (original message)

A link code was created.

Payload:

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

Edges:

  • generated_by → Platform User

A link code was used to link accounts.

Payload:

{
  "code": "1234-5678-9012-5924",
  "source_identity_id": "pu_abc123",
  "target_identity_id": "pu_xyz789",
  "merged_user_id": "user_merged123"
}

Edges:

  • source → Platform User (code owner)
  • target → Platform User (code redeemer)

User Events#

user.merged#

Two user records were merged.

Payload:

{
  "source_user_id": "user_old123",
  "target_user_id": "user_kept456",
  "merged_identities": ["pu_abc123", "pu_xyz789"]
}

Conversation Events#

conversation.created#

A conversation was created between participants. Conversations are auto-created when messages are exchanged between users.

Payload:

{
  "type": "conversation",
  "identities": ["identity-uuid-1", "identity-uuid-2"],
  "title": "Alice and Bob",
  "description": "Initially created via WhatsApp",
  "created_via_platform": "whatsapp",
  "mode": "private"
}
FieldTypeDescription
typestringAlways "conversation"
identitiesstring[]UserIdentity UUIDs of the initial participants
titlestringAuto-generated from participant names
descriptionstringHow the conversation was created
created_via_platformstringPlatform that triggered creation
modestringConversation mode (default: "private")

Edges:

  • participant → User (one per participant)
  • in_conversation → Self (conversation links to itself so it appears in its own event stream)

Call Events#

Recorded by a Twilio Voice connection. Each carries platform: "twilio_voice" and an in_conversation edge to the conversation between the caller and the system user, so a call sits in the same history as the pair’s messages. Each is written once per call: Twilio’s retries are settled on an idempotency key.

call.initiated#

startCall was accepted. Written before Twilio is asked to dial.

Payload:

{
  "platform": "twilio_voice",
  "direction": "outbound",
  "from": "+447700900000",
  "to": "+447700900123",
  "user_id": "user-uuid",
  "system_user_id": "system-user-uuid",
  "conversation_id": "conversation-uuid",
  "reason": "They asked to be called about the booking",
  "first_message": "Hi, it's about your booking",
  "metadata": {},
  "platform_connection_id": "pc-uuid",
  "call_sid": "CA...",
  "placement": "placed"
}
FieldDescription
placementplaced once Twilio accepted the call, failed if it refused, unknown if it timed out (the call may have been placed)
call_sidSet once Twilio accepted the call
errorPresent when placement is failed or unknown

Idempotency key: call:initiate:<idempotencyKey>

call.started#

The call was answered and its audio handed to the connection’s stream_url. The stream ticket names this event.

Payload:

{
  "platform": "twilio_voice",
  "call_sid": "CA...",
  "direction": "inbound",
  "transport": "pstn",
  "from": "+447700900123",
  "to": "+447700900000",
  "stir_verstat": "TN-Validation-Passed-A",
  "user_id": "user-uuid",
  "system_user_id": "system-user-uuid",
  "conversation_id": "conversation-uuid",
  "platform_connection_id": "pc-uuid"
}
FieldDescription
directioninbound or outbound
transportpstn, whatsapp (WhatsApp call bridged over SIP) or sip
stir_verstatTwilio’s caller-ID verification verdict (inbound only)
reason, first_message, metadataCopied from call.initiated (outbound only)
answered_byTwilio’s answering-machine verdict, when available (outbound only)

Origin: call.initiated for an outbound call; none for inbound. Idempotency key: call:<CallSid>:started

call.ended#

Twilio reported a terminal status. Calls rejected before call.started (unknown caller, withheld number) produce no call.ended.

Payload:

{
  "platform": "twilio_voice",
  "call_sid": "CA...",
  "status": "completed",
  "answered": true,
  "duration_seconds": 252,
  "direction": "inbound",
  "transport": "pstn",
  "user_id": "user-uuid",
  "system_user_id": "system-user-uuid",
  "conversation_id": "conversation-uuid",
  "answered_by": null,
  "sip_response_code": null
}
FieldDescription
statuscompleted, no-answer, busy, failed or canceled
answeredWhether a call.started exists for this call
duration_secondsCall length reported by Twilio

Origin: call.started, or call.initiated if the call was never answered. Idempotency key: call:<CallSid>:ended


Custom Events#

Custom events can use any valid name - no special prefix required.

Naming Rules#

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.*

Examples#

order.created

{
  "order_id": "order_12345",
  "total": 99.99,
  "currency": "USD",
  "items": [...]
}

payment.completed

{
  "payment_id": "pay_xyz",
  "amount": 99.99,
  "method": "credit_card"
}

support.ticket.opened

{
  "ticket_id": "ticket_123",
  "subject": "Help needed",
  "priority": "high"
}

purchase (single-word names are valid too)

{
  "item": "Premium Plan",
  "amount": 49.99
}

Media Types#

Messages can include different media types:

text#

{
  "type": "text",
  "text": "Hello!"
}

image#

{
  "type": "image",
  "media": {
    "id": "file_xyz",
    "mime_type": "image/jpeg",
    "url": "https://..."
  },
  "caption": "Check this out!"
}

video#

{
  "type": "video",
  "media": {
    "id": "file_xyz",
    "mime_type": "video/mp4",
    "url": "https://..."
  }
}

audio#

{
  "type": "audio",
  "media": {
    "id": "file_xyz",
    "mime_type": "audio/ogg",
    "url": "https://..."
  }
}

document#

{
  "type": "document",
  "media": {
    "id": "file_xyz",
    "mime_type": "application/pdf",
    "filename": "invoice.pdf",
    "url": "https://..."
  }
}

location#

{
  "type": "location",
  "location": {
    "latitude": 37.7749,
    "longitude": -122.4194,
    "name": "San Francisco",
    "address": "123 Main St"
  }
}

contact#

{
  "type": "contact",
  "contact": {
    "name": "John Doe",
    "phone": "+14155551234",
    "email": "john@example.com"
  }
}

Querying Events by Type#

The events query takes eventTypes, a list of exact event-type names.

Single Type#

query {
  events(eventTypes: ["message.inbound"], first: 20) {
    edges { node { id } }
  }
}

Multiple Types#

query {
  events(eventTypes: ["message.inbound", "message.outbound"], first: 20) {
    edges { node { id eventType } }
  }
}

Custom Events#

Pass the exact custom type names you want (there’s no prefix wildcard on the events query, so enumerate the types):

query {
  events(eventTypes: ["custom.order_shipped", "custom.order_delivered"], first: 20) {
    edges { node { id eventType payload } }
  }
}

To match by a payload field instead of type, use payloadFilters. See the GraphQL API reference for the full filter set.