@fivexer/sdk
v0.22.0
Published
Typed, zero-dependency TypeScript client for the Fivexer Platform /v1, /auth, /console, worker-portal, and AI portal-studio APIs, plus webhook signature verification
Readme
@fivexer/sdk (TypeScript)
Typed TypeScript client for the Fivexer Platform /v1 routing API. Zero runtime
dependencies (uses the global fetch available in Node ≥ 18, Deno, Bun, Cloudflare Workers, etc.).
Create workers and tasks; the platform matches continuously and notifies you via signed webhooks.
- Zero dependencies — global
fetch - Typed — full request/response types exported from
./types.js - Resilient — automatic retry on
429/5xxhonoringRetry-After, idempotency keys on creates - Observable — every response updates
client.quotafromX-Quota-*headers
Install
npm install @fivexer/sdkQuickstart
import { Fivexer } from '@fivexer/sdk';
const client = new Fivexer({
baseUrl: 'https://api.5xer.com',
apiKey: 'sk_test_...',
});
// Register a worker
await client.workers.upsert({ id: 'agent_1', tags: ['english', 'billing'] });
// Create a task — it is queued for matching
const task = await client.tasks.create({ tags: ['english', 'billing'], priority: 90 });
console.log(task.id, task.status); // task_8fk2 queued
// Inspect who got it, and why
await client.workers.queue('agent_1');
await client.decisions.list({ taskId: task.id });Error handling
Non-2xx responses throw FivexerApiError with the API's code and, on 402/429, the quota
snapshot:
import { FivexerApiError } from '@fivexer/sdk';
try {
await client.tasks.create({ tags: ['english'] });
} catch (e) {
if (e instanceof FivexerApiError) {
console.log(e.status, e.code); // 429 rate_limited
console.log(e.quota?.taskRateRemaining); // 0
}
}Webhooks
Webhook signature verification is Node-only and lives at the @fivexer/sdk/webhook subpath:
import { verifyWebhookSignature, SIGNATURE_HEADER } from '@fivexer/sdk/webhook';
const ok = verifyWebhookSignature(
'whsec_...',
request.headers[SIGNATURE_HEADER],
await request.text(),
);Onboarding workers
workers.upsert creates a routing record — it does not give a person a way to log
in. That is what invites, PIN identities and QR join links do, and all three are on
the /v1 client:
// Invite by email: a single-use magic link where they pick their own PIN.
const invite = await fivexer.workers.identities.invite({
email: '[email protected]',
label: 'Night Picker',
tags: ['warehouse'], // only applied when the invite creates the worker
});
invite.emailStatus; // 'sent' | 'mailer_unconfigured' | 'send_failed'
invite.inviteUrl; // share this yourself when no mailer is configured
// Who hasn't accepted yet?
const { identities } = await fivexer.workers.identities.list();
identities.filter((i) => !i.hasPin);
// Lost link? Resending invalidates every previous one.
await fivexer.workers.identities.resendInvite('w_123');
// No inbox to send to — set a PIN directly instead.
await fivexer.workers.identities.create('kiosk-1', { label: 'Kiosk 1', pin: '4821' });
// Where they log in.
const { portalUrl, portalEnabled } = await fivexer.portal();
// Self-registration for a crowd: one QR code, preset tags/skills/team.
const link = await fivexer.joinLinks.create({ label: 'Spring intake', tags: ['depot'], maxUses: 50 });
link.joinUrl; // encode into the QR; returned only here, at creationinviteUrl and joinUrl are credential-equivalent until consumed — anyone holding
one can set a PIN and act as that worker (a join link can create workers outright).
Treat them like API keys: prefer a short expiresInMs and a maxUses ceiling, and
leave requiresApproval on so new workers land paused until an operator resumes them.
Invited workers start off shift and are never matched until they accept and go
available themselves. All of this needs the hosted control plane and the worker
portal switched on for the workspace — portal() reports both, and the write calls
fail with worker_portal_disabled until an owner enables it in the console.
Worker portal
For worker-scoped sessions (a single worker's own login/token), use FivexerWorker:
import { FivexerWorker } from '@fivexer/sdk';
const worker = new FivexerWorker({ baseUrl: 'https://api.5xer.com' });
await worker.auth.login({ workspaceId: 'ws_1', workerId: 'agent_1', pin: '4821' });
await worker.tasks.accept(taskId);License
MIT.
