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#

GraphQL#

mutation CreateCustomEvent {
  createEvent(
    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: PLATFORM_USER
        targetNodeId: "pu_abc123"
      }
    ]
  ) {
    id
    eventType
    payload
    createdAt
    edges {
      edgeType
      targetNodeType
    }
  }
}

REST#

curl -X POST https://api.outeract.com/v1/events \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "order.created",
    "payload": {
      "order_id": "order_12345",
      "total": 99.99
    },
    "edges": [
      {
        "edge_type": "placed_by",
        "target_node_type": "platform_user",
        "target_node_id": "pu_abc123"
      }
    ]
  }'

Event Schemas#

Define schemas to validate custom event payloads.

Create Schema#

mutation CreateOrderSchema {
  createEventSchema(
    name: "order.created"
    description: "Order creation event"
    jsonSchema: {
      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" }
            }
          }
        }
      }
    }
    enforceValidation: true
  ) {
    id
    name
  }
}

Schema Validation#

When enforceValidation: true, 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(
    filters: {
      eventType: { equals: "order.created" }
    }
    first: 20
  ) {
    edges {
      node {
        id
        payload
        createdAt
      }
    }
  }
}

By Payload Field#

query {
  events(
    filters: {
      eventType: { startsWith: "order" }
      payload: {
        path: "total"
        gte: 100
      }
    }
  ) {
    edges {
      node {
        id
        payload
      }
    }
  }
}
query {
  events(
    filters: {
      eventType: { equals: "order.created" }
      hasEdge: {
        edgeType: "placed_by"
        targetNodeId: { equals: "pu_abc123" }
      }
    }
  ) {
    edges {
      node {
        id
        payload
        edges {
          edgeType
          targetPlatformUser {
            externalId
            name
          }
        }
      }
    }
  }
}

Linking Events#

mutation {
  createEvent(
    eventType: "support.ticket.opened"
    payload: {
      ticket_id: "ticket_123"
      subject: "Help with order"
      priority: "high"
    }
    edges: [
      {
        edgeType: "opened_by"
        targetNodeType: PLATFORM_USER
        targetNodeId: "pu_abc123"
      }
    ]
  ) {
    id
  }
}
mutation {
  createEvent(
    eventType: "order.shipped"
    payload: {
      order_id: "order_12345"
      tracking_number: "1Z999AA10123456784"
    }
    originEventId: "evt_order_created_123"
  ) {
    id
    originEvent {
      id
      eventType
    }
  }
}
mutation {
  createEvent(
    eventType: "document.uploaded"
    payload: {
      document_type: "invoice"
      document_number: "INV-2024-001"
    }
    edges: [
      {
        edgeType: "attachment"
        targetNodeType: FILE
        targetNodeId: "file_xyz789"
      }
    ]
  ) {
    id
  }
}

Subscribing to Custom Events#

Webhook Subscription#

mutation {
  createWebhookSubscription(
    name: "Order Events"
    targetUrl: "https://myapp.com/webhooks/orders"
    eventPatterns: ["order.*"]
    secret: "webhook-secret"
  ) {
    id
  }
}

Webhook Payload#

{
  "id": "whd_abc123",
  "event_id": "evt_xyz789",
  "event_type": "order.created",
  "payload": {
    "order_id": "order_12345",
    "total": 99.99,
    "currency": "USD"
  },
  "edges": {
    "placed_by": {
      "platform_user_id": "pu_abc123",
      "external_id": "+14155551234"
    }
  },
  "created_at": "2024-01-15T10:30:00Z"
}

Use Cases#

E-commerce Integration#

# When order is placed
async def create_order_event(order, platform_user_id):
    await client.execute("""
        mutation CreateOrderEvent($payload: JSON!, $platformUserId: UUID!) {
            createEvent(
                eventType: "order.created"
                payload: $payload
                edges: [{
                    edgeType: "placed_by"
                    targetNodeType: PLATFORM_USER
                    targetNodeId: $platformUserId
                }]
            ) { id }
        }
    """, {
        "payload": {
            "order_id": order.id,
            "total": order.total,
            "items": order.items
        },
        "platformUserId": platform_user_id
    })

# When order ships
async def create_shipped_event(order, original_event_id):
    await client.execute("""
        mutation CreateShippedEvent($payload: JSON!, $originId: UUID!) {
            createEvent(
                eventType: "order.shipped"
                payload: $payload
                originEventId: $originId
            ) { id }
        }
    """, {
        "payload": {
            "order_id": order.id,
            "tracking_number": order.tracking
        },
        "originId": original_event_id
    })

Support Ticketing#

async def create_ticket_events(ticket, platform_user_id):
    # Ticket opened
    result = await client.execute("""
        mutation {
            createEvent(
                eventType: "support.ticket.opened"
                payload: {
                    ticket_id: "%s"
                    subject: "%s"
                    priority: "%s"
                }
                edges: [{
                    edgeType: "opened_by"
                    targetNodeType: PLATFORM_USER
                    targetNodeId: "%s"
                }]
            ) { id }
        }
    """ % (ticket.id, ticket.subject, ticket.priority, platform_user_id))

    return result["createEvent"]["id"]

Analytics Events#

async def track_user_action(action, platform_user_id, metadata=None):
    await client.execute("""
        mutation TrackAction($eventType: String!, $payload: JSON!, $puId: UUID!) {
            createEvent(
                eventType: $eventType
                payload: $payload
                edges: [{
                    edgeType: "performed_by"
                    targetNodeType: PLATFORM_USER
                    targetNodeId: $puId
                }]
            ) { id }
        }
    """, {
        "eventType": f"analytics.{action}",
        "payload": {
            "action": action,
            "timestamp": datetime.utcnow().isoformat(),
            **(metadata or {})
        },
        "puId": platform_user_id
    })

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.

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.