Now let’s close the loop and have our service publish events. We will emit an Orders.Shipped event so downstream services, such as a warehouse system, notifications service, or billing system, can react without being coupled directly to us.

Add the event schema to schemas.ts:

export const OrderShippedEvent = Type.Object({
  orderId: Type.String(),
  customerId: Type.String(),
  shippedAt: Type.String({ format: "date-time" }),
});
export type OrderShippedEvent = Static<typeof OrderShippedEvent>;

Register the event in contracts/orders_service.ts:

import * as schemas from "../schemas.ts";

// ...

// In the first defineServiceContract argument:
{ schemas }

// In the returned contract body:
  events: {
    "Orders.Shipped": {
      version: "v1",
      event: ref.schema("OrderShippedEvent"),
      capabilities: { publish: ["service"], subscribe: ["service"] },
    },
  },

The capabilities here mean: only service-tier principals may publish or subscribe. Other services that list Orders.Shipped in their own uses blocks will receive the events automatically.

If an event subject includes payload tokens through params, each JSON Pointer must resolve to a string, number, or integer-compatible schema. When the event payload schema is an anyOf or oneOf union, every variant must contain the pointer; otherwise some valid payloads could not produce the declared subject token.

Now publish the event from main.ts. Here we call it after marking an order as fulfilled. You would do this wherever your fulfillment logic lives:

await service.event.orders.shipped.publish({
  orderId,
  customerId,
  shippedAt: new Date().toISOString(),
});

event.orders.shipped.publish(...) validates the payload against OrderShippedEvent before sending. If the shape is wrong, it returns a Result.err(...) rather than silently dropping the event.

Upgrade the contract and restart.