Relay

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:

  1. Type lookup -- Verify the event type exists in the catalog.
  2. Deprecation check -- Reject deprecated event types.
  3. Schema validation -- If the event type has a JSON Schema, validate Data against 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:

  1. Resolve endpoints for the tenant whose subscription patterns match the event type.
  2. Create a Delivery per matched endpoint.
  3. 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)
}

On this page