Custom Events#
Create custom event types to track application-specific data alongside messaging events.
Overview#
While Outeract automatically creates events for messages, you can create custom events for:
- Order updates
- Payment confirmations
- Support ticket status
- User actions
- System events
Naming Rules#
Custom events can use any valid name - no special prefix required.
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
payment.completed
support.ticket.opened
subscription.upgraded
purchase
analytics.page_view**Convention:** Many apps use a prefix like `order.*` or `support.*` to namespace events by domain. This is recommended but not required.
Creating Custom Events#
Events are created in a user’s event stream, so userId is required. Additional graph edges are optional:
mutation CreateCustomEvent {
createEvent(
userId: "123e4567-e89b-12d3-a456-426614174000"
eventType: "order.created"
payload: {
order_id: "order_12345"
customer_id: "cust_xyz"
total: 99.99
currency: "USD"
items: [
{ sku: "WIDGET-001", quantity: 2, price: 49.99 }
]
}
edges: [
{
edgeType: "placed_by"
targetNodeType: "identity"
targetNodeId: "identity-uuid"
}
]
) {
id
eventTypeName
payload
createdAt
edges {
edgeType
targetNodeType
}
}
}Event Schemas#
Define schemas to validate custom event payloads.
Create Schema#
Event schemas are managed in the console under Settings → Event Schemas. A schema has a name and an optional JSON Schema document that inbound payloads are validated against:
Name: order.created
JSON Schema:
{
"type": "object",
"required": ["order_id", "total"],
"properties": {
"order_id": {
"type": "string",
"pattern": "^order_[a-zA-Z0-9]+$"
},
"total": {
"type": "number",
"minimum": 0
},
"currency": {
"type": "string",
"enum": ["USD", "EUR", "GBP"],
"default": "USD"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sku": { "type": "string" },
"quantity": { "type": "integer", "minimum": 1 },
"price": { "type": "number" }
}
}
}
}
}Enable Enforce validation on the schema to reject non-conforming events.
Schema Validation#
With enforced validation, events that don’t match the schema are rejected:
{
"errors": [
{
"message": "Validation failed: 'total' is required",
"extensions": {
"code": "VALIDATION_ERROR",
"path": ["payload", "total"]
}
}
]
}Querying Custom Events#
By Event Type#
query {
events(
eventTypes: ["order.created"]
first: 20
) {
edges {
node {
id
payload
createdAt
}
}
}
}By Payload Field#
Payload filters match on field equality:
query {
events(
eventTypes: ["order.created"]
payloadFilters: [{ field: "order_id", value: "order_12345" }]
) {
edges {
node {
id
payload
}
}
}
}With Related Entities#
Filter to events that have an edge targeting a specific node:
query {
events(
eventTypes: ["order.created"]
relatedNodeId: "identity-uuid"
relatedNodeType: "identity"
relatedEdgeType: "placed_by"
) {
edges {
node {
id
payload
edges {
edgeType
targetNodeType
targetNodeId
}
}
}
}
}Linking Events#
Link to an Identity#
mutation {
createEvent(
userId: "user-uuid"
eventType: "support.ticket.opened"
payload: {
ticket_id: "ticket_123"
subject: "Help with order"
priority: "high"
}
edges: [
{
edgeType: "opened_by"
targetNodeType: "identity"
targetNodeId: "identity-uuid"
}
]
) {
id
}
}Link to Previous Event#
mutation {
createEvent(
userId: "user-uuid"
eventType: "order.shipped"
payload: {
order_id: "order_12345"
tracking_number: "1Z999AA10123456784"
}
originEventId: "origin-event-uuid"
) {
id
originEventId
}
}Link to File#
mutation {
createEvent(
userId: "user-uuid"
eventType: "document.uploaded"
payload: {
document_type: "invoice"
document_number: "INV-2024-001"
}
edges: [
{
edgeType: "attachment"
targetNodeType: "file"
targetNodeId: "file-uuid"
}
]
) {
id
}
}Subscribing to Custom Events#
Webhook Subscription#
In the console under Settings → Webhooks, create a subscription named Order Events pointing at https://myapp.com/webhooks/orders, matching the event type pattern order.*.
Webhook Payload#
{
"event_id": "123e4567-e89b-12d3-a456-426614174000",
"event_type": "order.created",
"timestamp": "2024-01-15T10:30:00Z",
"app_id": "your-app-uuid",
"data": {
"order_id": "order_12345",
"total": 99.99,
"currency": "USD"
},
"edges": [
{
"edge_type": "placed_by",
"target_node_type": "identity",
"target_node_id": "identity-uuid"
}
]
}Use Cases#
E-commerce Integration#
# When order is placed
async def create_order_event(order, user_id, identity_id):
await client.execute("""
mutation CreateOrderEvent($userId: UUID!, $payload: JSON!, $identityId: UUID!) {
createEvent(
userId: $userId
eventType: "order.created"
payload: $payload
edges: [{
edgeType: "placed_by"
targetNodeType: "identity"
targetNodeId: $identityId
}]
) { id }
}
""", {
"userId": user_id,
"payload": {
"order_id": order.id,
"total": order.total,
"items": order.items
},
"identityId": identity_id
})
# When order ships
async def create_shipped_event(order, user_id, original_event_id):
await client.execute("""
mutation CreateShippedEvent($userId: UUID!, $payload: JSON!, $originId: UUID!) {
createEvent(
userId: $userId
eventType: "order.shipped"
payload: $payload
originEventId: $originId
) { id }
}
""", {
"userId": user_id,
"payload": {
"order_id": order.id,
"tracking_number": order.tracking
},
"originId": original_event_id
})Support Ticketing#
async def create_ticket_events(ticket, user_id):
# Ticket opened
result = await client.execute("""
mutation OpenTicket($userId: UUID!, $payload: JSON!) {
createEvent(
userId: $userId
eventType: "support.ticket.opened"
payload: $payload
) { id }
}
""", {
"userId": user_id,
"payload": {
"ticket_id": ticket.id,
"subject": ticket.subject,
"priority": ticket.priority
}
})
return result["createEvent"]["id"]Analytics Events#
async def track_user_action(action, user_id, metadata=None):
await client.execute("""
mutation TrackAction($userId: UUID!, $eventType: String!, $payload: JSON!) {
createEvent(
userId: $userId
eventType: $eventType
payload: $payload
) { id }
}
""", {
"userId": user_id,
"eventType": f"analytics.{action}",
"payload": {
"action": action,
"timestamp": datetime.utcnow().isoformat(),
**(metadata or {})
}
})Best Practices#
1. Consistent Naming#
Use a clear hierarchy: {domain}.{action} or {domain}.{subdomain}.{action}
2. Include Timestamps#
Add timestamps in payload for events with delayed processing.
3. Link Everything#
Use edges to connect events to users, files, and other events.
4. Define Schemas#
Create schemas for validation and documentation.
5. Use Origin Events#
Chain related events using originEventId.
6. Keep Payloads Focused#
Include relevant data, not entire objects.