Skip to main content

Agent Client SDK

@zihin/agent-client is the official Node.js client for invoking Zihin agents. It is the shared core behind every Zihin connector (n8n, Activepieces, Zapier) and the reference client for your own backends. Zero platform dependencies.

Rolling out

@zihin/agent-client is being published to npm. The API documented here is final.

npm install @zihin/agent-client

Requires Node ≥ 18 (uses global fetch / ReadableStream).

Create a client

import { createClient } from '@zihin/agent-client';

const zihin = createClient({
apiKey: process.env.ZIHIN_API_KEY, // zhn_live_* — tenant is embedded
});
OptionDefaultDescription
apiKeyRequired. zhn_live_* / zhn_test_*.
baseUrlhttps://llm.zihin.aiOverride for a private deployment.
timeoutMs120000Max total time for the whole call (headers and stream).
maxRetries2Retries with exponential backoff.
allowCustomBaseUrlfalseAllow a baseUrl outside the Zihin domain whitelist (SSRF opt-in).
allowHttpfalseAllow http/localhost (development only).
fetchglobalInject a fetch for runtimes without one (edge/serverless).
onRetry / onErrorObservability hooks (no console).

Direct invocation

Wait for the result. The client buffers the SSE stream and consolidates the response and metrics.

const res = await zihin.invokeAgent({
agentId: 'uuid-of-the-agent',
message: 'What is the sales forecast?',
sessionId: previousSessionId, // optional — continues the conversation
});

res.content; // final text
res.sessionId; // reuse on the next message for continuity
res.model; // effective model
res.usage; // { inputTokens, outputTokens, totalTokens, costUsd }
res.sources; // tool / retrieval sources, when present

Streaming

Receive deltas as the agent thinks, calls tools, and answers:

for await (const chunk of zihin.streamAgent({ agentId, message: 'Hello' })) {
if (chunk.type === 'token') process.stdout.write(chunk.content);
if (chunk.type === 'tool') console.log('tool:', chunk.tool);
}
Session continuity

Pass the sessionId returned by a previous call to keep context across turns. Leave it empty to start a fresh conversation.

Asynchronous invocation (triggers)

For long-running or decoupled work, provision a webhook bound to the agent and dispatch to it. The dispatch returns immediately; the result arrives at your callbackUrl (or via polling).

// Once — provision (or reuse) a webhook. Prefer findOrCreateTrigger: the backend has no name
// uniqueness, so a repeated createTrigger would create duplicates.
const { triggerId, webhookUrl } = await zihin.findOrCreateTrigger({
agentId,
name: 'Integration X',
mode: 'async',
callbackUrl: 'https://my-platform.com/webhook/zihin',
});

// N times — dispatch (responds 202; the result is delivered to the callback)
const { executionId } = await zihin.dispatchAgent({
triggerId,
payload: { message: 'Process this order' },
});

// Optional — poll the result instead of using a callback
const exec = await zihin.getExecution({ triggerId, executionId });

mode: 'sync' instead responds 200 + JSON immediately. Trigger lifecycle helpers: listTriggers, getTrigger, deleteTrigger.

List agents

const agents = await zihin.listAgents(); // GET /api/v2/agents
// [{ id, name, commercialName, status, toolsCount, ... }]

Useful to populate an agent picker in a connector UI.

API key permissions

OperationRequired role
invokeAgent / streamAgentany role
createTrigger / getExecutionadmin or editor

A member key calling a restricted operation gets a ZihinAgentError with kind: 'auth'.

Errors

Every failure is a ZihinAgentError:

FieldMeaning
kindconfig · auth · not_found · rate_limit · timeout · aborted · server · network · protocol · unknown
statusHTTP status, when applicable
retryablewhether a retry could succeed
endpoint"POST host/path" for debugging
requestIdbackend handle — share it with support to correlate

Messages redact only secrets (key / Bearer / stack); request_id and trace_id are preserved. Network errors carry the root cause (ECONNREFUSED / ENOTFOUND / TLS…). Cancel any call with an AbortSignal via params.signal.

Robustness

  • Timeout covers everything — headers and the entire body/stream. timeoutMs (default 120 s, matching the backend cap) bounds the whole call.
  • Retries never duplicate effects — idempotent reads retry on network/429/5xx; side-effecting POSTs (invokeAgent, dispatchAgent, createTrigger) retry only on 429/503 and never on a network error (the request may already have been processed). Retry-After is honored.
  • Memory safety — stream limits (maxStreamEventBytes 1 MB, maxStreamTotalBytes 64 MB) abort with kind: 'protocol' if exceeded.
  • Transport safetyredirect: 'error' (a cross-host 3xx would leak the key) and cache: 'no-store' on every request.

Security

The client validates the base (and callback) URL by hostname against the Zihin whitelist. Set allowCustomBaseUrl: true only for a trusted private deployment — the API key is sent to that host.

warning

With allowCustomBaseUrl: true, a host that resolves to a private IP (DNS rebinding) will pass. Keep the whitelist on in sensitive environments.