Skip to content

Node.js SDK

The Layr8 Node.js SDK is a TypeScript/ESM library for building DIDComm agents. It handles WebSocket connections, Phoenix Channel protocol, DIDComm message formatting, and thread correlation. You write domain logic.

A basic agent is ~30 lines of TypeScript. The SDK manages everything between your handler and the network.

Install

Terminal window
npm install @layr8/sdk

Requires Node.js >= 20 and ESM ("type": "module" in package.json).

Core Concepts

Config

import { Layr8Client } from "@layr8/sdk";
const client = new Layr8Client({
nodeUrl: "wss://your-node.node.layr8.io/plugin_socket/websocket",
apiKey: "your-api-key",
agentDid: "did:web:your-node.node.layr8.io:my-agent",
});

All fields fall back to environment variables if empty: LAYR8_NODE_URL, LAYR8_API_KEY, LAYR8_AGENT_DID. If agentDid is omitted, the node assigns an ephemeral DID on connect.

Handlers

Register handlers before calling connect. The handler’s return value determines what happens:

import type { Message } from "@layr8/sdk";
client.handle(
"https://layr8.io/protocols/echo/1.0/request",
async (msg: Message): Promise<Message | null> => {
// Return Message → send response to sender
// Return null → no response (fire-and-forget)
// Throw → send problem report to sender
},
);

The protocol base URI is derived automatically from the message type and registered with the node on connect.

Send (Fire-and-Forget)

await client.send({
type: "https://didcomm.org/basicmessage/2.0/message",
to: ["did:web:partner-node:agent"],
body: { content: "Hello!" },
});

send accepts Partial<Message> — only type, to, and body are required.

Request (Request/Response)

const resp = await client.request(
{
type: "https://layr8.io/protocols/echo/1.0/request",
to: ["did:web:partner-node:echo-agent"],
body: { message: "ping" },
},
{ signal: AbortSignal.timeout(5_000) },
);
// resp is the correlated response, matched by thread ID

Messages

interface Message {
id: string; // auto-generated if empty
type: string; // DIDComm message type URI
from: string; // auto-filled with agent DID
to: string[]; // recipient DIDs
threadId: string; // auto-generated for request
parentThreadId: string;
body: unknown; // serialized to JSON
context?: MessageContext;
}

Use unmarshalBody<T>(msg) to deserialize the body with type safety. On inbound messages, msg.context provides node metadata: authorized, recipient, and senderCredentials.

Durable Handlers (Persist-then-Ack)

For messages that must not be lost, use manual ack to persist before acknowledging:

import { ack } from "@layr8/sdk";
client.handle(
"https://layr8.io/protocols/order/1.0/created",
async (msg: Message): Promise<null> => {
// Write to disk first — if this throws, the message is
// NOT acked and the cloud-node will redeliver it.
appendFileSync("messages.jsonl", JSON.stringify(msg) + "\n");
ack(msg as any); // safe to ack now
return null;
},
{ manualAck: true },
);

See examples/durable-handler.ts for a complete working example.

Claude Code Skill

The SDK includes a Claude Code skill that teaches Claude the full API. Install it into any agent project:

Terminal window
mkdir -p .claude/skills
cp /path/to/node-sdk/.claude/skills/build-layr8-agent-node.md .claude/skills/

Once installed, Claude Code knows how to build Layr8 agents in every session. Example prompts:

  • “Build me a webhook relay agent that forwards DIDComm messages to my REST API”
  • “Add a handler for order.created messages that validates the sender’s credentials”
  • “Convert this Express endpoint into a DIDComm agent”