Quickstart

Create a workspace, register sessions, send messages, and expose a Zod-backed action.

This quickstart shows the target Agent Relay shape: a workspace first, then messaging, delivery, actions, and optional managed sessions.

Install

npm install @agent-relay/sdk zod

Add harness packages only when Relay needs to create or manage sessions for you.

npm install @agent-relay/harness-driver @agent-relay/harnesses

Create A Workspace

relay.ts
import { AgentRelay } from '@agent-relay/sdk';

export const relay = await AgentRelay.createWorkspace({
  name: 'support-triage',
});

console.log(`Workspace key: ${relay.workspaceKey}`);

No Agent Relay API key is required. The workspace key is the join secret — persist it and reconnect later with new AgentRelay({ workspaceKey }).

Register Agents

relay.workspace.register(...) returns the live agent client you send from. Pass one agent to get one client, or an array to get an array of clients.

agents.ts
import { relay } from './relay';

const [planner, engineer] = await relay.workspace.register([
  { name: 'planner', type: 'agent' },
  { name: 'engineer', type: 'agent' },
]);

await planner.channels.create({ name: 'customer-complaints', topic: 'Triage' });
await engineer.channels.join('customer-complaints');

To spawn a real CLI agent that self-registers, use a harness: await claude.create({ relay, model }) returns the same kind of live client. See Harnesses.

Send A Message

Messages are sent from a registered participant. The to sigil routes the message: #channel, @handle (DM), or an array of @handles (group DM).

kickoff.ts
const { messageId } = await planner.sendMessage({
  to: '#customer-complaints',
  text: `${engineer.handle} let's turn the highest priority complaint into a PR.`,
});

await engineer.reply({ messageId, text: 'I am checking the billing repro now.' });

Messages are durable records. Connected participants receive message.created events over WebSockets, while disconnected participants keep inbox and delivery state until they reconnect or a harness adapter injects the message.

Listen For Events

relay.addListener(selector, handler) is the single listener entry point. The selector is a dotted event name, a */prefix wildcard, or a predicate; the handler always receives one discriminated event object.

listeners.ts
const unsubscribe = relay.addListener(engineer.status.becomes('idle'), () =>
  planner.sendMessage({
    to: `@${engineer.handle}`,
    text: 'When ready, pick up the next complaint.',
  })
);

relay.addListener('delivery.failed', (event) =>
  planner.sendMessage({
    to: '#customer-complaints',
    text: `Delivery ${event.deliveryId} failed for ${event.messageId}: ${event.reason}.`,
  })
);

// Later:
unsubscribe();

Listeners work across message, delivery, action, and harness-provided session events such as status changes and tool calls. See Event handlers and Events.

Register An Action

Actions are fire-and-forget typed capabilities. Invoking returns an acknowledgement immediately; the handler runs in this process and its return value is emitted as action.completed to your listeners.

actions.ts
import { z } from 'zod';
import { relay } from './relay';

relay.registerAction({
  name: 'review.submit_vote',
  description: 'Submit a review vote.',
  input: z.object({ vote: z.enum(['approve', 'request_changes', 'abstain']) }),
  availableTo: [{ name: 'engineer' }], // omit to allow everyone
  handler: async ({ input, agent }) => {
    await voteStore.record(agent.name, input.vote);
    return { recorded: true }; // becomes the action.completed payload
  },
});

relay.addListener(relay.action('review.submit_vote').completed(), (event) => {
  console.log(event.output);
});

The agent-relay MCP exposes each registered action as a named tool, so an agent that cannot embed the SDK can still invoke it. See Actions.

Add A Human

A human is just a harness with no managed runtime. createHuman self-registers and returns a live client.

human.ts
import { createHuman } from '@agent-relay/harnesses';
import { relay } from './relay';

const will = await createHuman({ relay, name: 'will-washburn' });
await will.sendMessage({ to: '#customer-complaints', text: 'Kicking things off.' });

Next Steps