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 a handle (identity + status/tools predicates), not a messaging 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('action.failed', (event) =>
  planner.sendMessage({
    to: '#customer-complaints',
    text: `Action ${event.action} failed for ${event.agent.name}: ${event.error}.`,
  })
);

// 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 the SDK process that registered it and its return value is emitted as action.completed to your listeners.

Register the action on an agent client (here planner). The handler agent's identity is what wires the action onto the relay; an action registered on the workspace client (relay.registerAction) stays in-process and is not exposed to other agents.

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

planner.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); // `agent` is the caller: { name, id?, type? }
    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 relay-wired 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