Events

Events are immutable facts about something that happened in your system.

Writing an event

Events are written via HTTP POST to /{aggregate_type}/{aggregate_id}/{event_type}:

curl -X POST https://myapp.j17.dev/order/550e8400-e29b-41d4-a716-446655440000/was_placed \
  -H "Authorization: Bearer $J17_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "items": [{ "sku": "WIDGET-1", "quantity": 2 }],
      "total": 59.98
    },
    "metadata": {
      "actor": { "type": "user", "id": "550e8400-e29b-41d4-a716-446655440001" }
    }
  }'

Every event requires:

  • aggregate_type - In the URL path (e.g., order)
  • aggregate_id - In the URL path (UUIDv4 or humane code)
  • event_type - In the URL path (e.g., was_placed)
  • data - The event payload
  • metadata.actor - Who or what caused this event

Metadata structure

Every event includes metadata:

Field Required Description
actor Yes { "type": "user", "id": "..." } - who caused this event
target No { "type": "item", "id": "..." } - what the action targeted
previous_length No Expected event count for external OCC

Actor and target types must be declared in your spec's agent_types and target_types arrays.

Events are immutable

Once written, an event can never be changed or deleted. This is the core principle of event sourcing.

To "undo" something, write a new event representing the reversal:

curl -X POST https://myapp.j17.dev/order/550e8400-.../was_cancelled \
  -H "Authorization: Bearer $J17_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": { "reason": "Customer request" },
    "metadata": {
      "actor": { "type": "user", "id": "..." }
    }
  }'

The order isn't deleted -- both was_placed and was_cancelled exist in history. The aggregate state reflects the cancellation because the handler for was_cancelled updates the status.

Naming conventions

Use snake_case past-tense verbs that describe what happened:

  • was_created not create
  • had_email_updated not updateEmail
  • was_cancelled not cancel

This makes it clear that events are facts about the past, not commands requesting future action.

Can't find what you need? support@j17.app