Skip to main content
Teams build managed agents in Ekanos: agents with their own instructions, tools and approval policy. An agent starts from a trigger. Declare agent events and every team that connects your integration can pick one as a trigger: “when a payment fails, start the billing agent”. Declare an agent channel too and the agent answers where the event happened, for example as a note on the payment. Your integration raises events; Ekanos does the rest. It validates each event, records it once, matches it against every team’s triggers, starts the agents, gives each run a reply tool bound to the right place, and runs every reply through its write gate (approval or pre-authorization, idempotency, audit). Your code never starts an agent and never chooses where a reply goes.

Declare an event

defineAgentEvent only types the functions by the payload schema. defineIntegration validates the declaration with the rest:

Raise it from a webhook

Return the events a delivery raises in the webhook result:
  • The host records each event before acknowledging the delivery. Agents start after the response, so a slow agent never delays your webhook.
  • dedupeKey is scoped to the account. Use the vendor’s delivery or event id, or whatever makes this occurrence unique (issue id + label id).
  • An event your integration doesn’t declare, or whose payload fails its schema, is logged and dropped; the delivery is still acknowledged. If the host can’t record an event, the delivery fails with a retryable error, and the dedupe key makes the retry safe.
  • At most 20 events per delivery.

Declare a channel

A channel is how a run answers. The host gives the run a reply tool named by you, bound to the target your event’s conversation() returned. The model writes the reply; it never picks the target.
deliver and the hooks get the same ctx as your other handlers, built for the account the run belongs to, with your declared egress enforced. reply.interim is true for a progress note; the answer comes in its own reply. If a run completes without answering, the host posts its final output through deliver for you.

Hand-off to a person

When a reply needs approval and a reviewer rejects the draft, the host calls hooks.handoff(ctx, target, { reason }, run) so the conversation goes back to a human the vendor’s way: for a help desk, an internal note with the reason and the ticket assigned to a human group. reason is the reviewer’s own note, or text the host composes from fixed fields, never model prose, so post it internally and never to the customer. The hook is not called when an approval merely expires. Like the other hooks it is best-effort.

Routing and the unrouted hook

If a Source connects your integration once for all its teams, its events are routed to a team by a key you declare, and hooks.unrouted posts a fixed note on an event no team is mapped to yet. See Routing and Source connections. A channel needs at least one event with a conversation. Events without a channel still work: those runs answer in Ekanos only.

Try it in the harness

The Triggers page lists your agent events and channel. Deliver a webhook and the result shows every event it raised, checked the way the host checks it: declared, payload valid, not your own activity, and where a run it starts would answer. The harness starts no agents.
The full example is packages/integrations-examples/acme-payments (src/events/agent.ts).