Events
Event submission, idempotency, and the validation pipeline.
Events are webhook payloads submitted for delivery. The Send() method validates, persists, and fans out each event.
Sending an event
err := r.Send(ctx, &event.Event{
Type: "order.created",
TenantID: "tenant-acme",
Data: json.RawMessage(`{"order_id":"ORD-001"}`),
IdempotencyKey: "order-ORD-001",
})Validation pipeline
Send() executes these checks in order:
- Type lookup -- Verify the event type exists in the catalog.
- Deprecation check -- Reject deprecated event types.
- Schema validation -- If the event type has a JSON Schema, validate
Dataagainst it.
Idempotency
The IdempotencyKey field prevents duplicate event processing. If a key has already been used, Send() returns nil (no-op success). This allows safe retries from the caller.
Fan-out
After validation, Send() resolves matching endpoints and creates one Delivery record per endpoint in pending state:
- Resolve endpoints for the tenant whose subscription patterns match the event type.
- Create a
Deliveryper matched endpoint. - The delivery engine picks up pending deliveries on its next poll cycle.
Event entity
type Event struct {
entity.Entity
ID id.ID `json:"id"`
Type string `json:"type"`
TenantID string `json:"tenant_id"`
Data any `json:"data"`
IdempotencyKey string `json:"idempotency_key,omitempty"`
}Store interface
type Store interface {
CreateEvent(ctx context.Context, evt *Event) error
GetEvent(ctx context.Context, evtID id.ID) (*Event, error)
ListEvents(ctx context.Context, opts ListOpts) ([]*Event, error)
}