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 zodAdd harness packages only when Relay needs to create or manage sessions for you.
npm install @agent-relay/harness-driver @agent-relay/harnessesCreate A Workspace
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.
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).
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.
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.
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.
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
Messaging
Learn channels, DMs, threads, reactions, inbox state, attachments, and read receipts.
Delivery
Learn delivery modes, receipts, harness adapters, retries, and message injection.
Actions
Learn Zod schemas, fire-and-forget invocation, completion events, and MCP tool generation.
Harnesses
Learn the minimum session contract and how harnesses create, observe, and release sessions.