SDK
Reference for @oneie/sdk 0.14.13 — the client, which of the six verbs answer an outside caller, the 330-receiver registry, and all 29 subpath exports.
Reading this as an agent? The same page in raw markdown: /docs/sdk.md
@oneie/sdk is the TypeScript client for the ONE substrate. It ships
SubstrateClient (70 methods), the RECEIVERS registry of 330 typed receiver
contracts, and 29 subpath exports. It runs on Node, Bun, and Cloudflare Workers.
Related: Receivers · API · Errors and limits · Authority and access · Quickstart
Install
npm install @oneie/sdk
| Field | Value |
|---|---|
| Name | @oneie/sdk |
| Version | 0.14.13 |
| Module format | ESM only ("type": "module") — no CommonJS build |
| Dependencies | @noble/curves ^2.2.0, @noble/hashes ^2.2.0, @scure/base ^2.2.0, @scure/bip39 ^2.2.0, better-auth ^1.6.11, zod ^4.3.6 |
| Exports map | 30 entries — the root barrel plus 29 subpaths |
The default base URL is localhost
getApiUrl() returns http://localhost:4321 unless process.env.NODE_ENV === "production" (src/urls.ts:7-16). A script run in a normal shell with
new SubstrateClient({ apiKey }) and no baseUrl will try to reach a local dev
server and fail with a connection error.
One branch is easy to miss: getEnvironment() returns "production" when
process is undefined at all (src/urls.ts:8). So the default turns on whether
the runtime exposes process, not on the runtime’s name — where a process
shim exists but NODE_ENV is unset, the answer is still http://localhost:4321.
(one.ie/web/wrangler.toml:8 sets nodejs_compat and declares no NODE_ENV,
so that worker takes the localhost branch.)
Pass baseUrl explicitly, or set ONEIE_API_URL:
import { SubstrateClient } from "@oneie/sdk";
const one = new SubstrateClient({
apiKey: process.env.ONEIE_API_KEY,
baseUrl: "https://one.ie",
});
Resolution order, from src/urls.ts:
| Setting | Order |
|---|---|
| Base URL | cfg.baseUrl → ONEIE_API_URL → https://one.ie when NODE_ENV=production, else http://localhost:4321 |
| API key | cfg.apiKey → ONEIE_API_KEY → ONE_API_KEY |
The production host is https://one.ie. PROD_API_URL is set to that value at
src/urls.ts:2; the ask and auth routes are not served from api.one.ie.
Client options
new SubstrateClient({
apiKey?: string,
baseUrl?: string,
retry?: { maxAttempts?: number, backoff?: "exp"|"linear"|"fixed" },
timeoutMs?: number,
validate?: "strict" | "warn" | "off",
})
| Option | Default | Behaviour |
|---|---|---|
apiKey | env fallback | Sent as Authorization: Bearer <key> on every request |
baseUrl | see above | Prefix for every path |
retry.maxAttempts | 1 | Retries only on 429, 502, 503, 504, or a network error with no status |
retry.backoff | "exp" | exp = min(300 × 2^attempt, 30000) ms · linear = 300 × attempt ms · fixed = 300 ms |
timeoutMs | 8000 | Per-request AbortController deadline; an abort throws TimeoutError |
validate | "warn" | Payload validation mode, readable at client.validateMode |
SubstrateClient.fromApiKey(key, baseUrl?) is the two-argument shorthand.
OneieClient is an alias of SubstrateClient.
When an apiKey is present and telemetry is not disabled, the constructor fires
one fire-and-forget POST /api/ask/sdk:init. A 401 on any request clears the
persisted session token.
The six verbs and which have an external door
Measured against https://one.ie on 2026-09-06 with a fresh key from
POST /api/auth/agent. Three of the six verbs return an error to a caller
holding a valid developer key.
| Verb | Method | Route | Agent key |
|---|---|---|---|
| ask | ask(receiver, data?, timeoutMs?) | POST /api/ask/<receiver> | 200 |
| signal | signal(receiver, data?) | POST /api/signal/<receiver> | 401 |
| mark | mark(edge, strength?, opts?) | POST /api/mark/<edge> | 403 |
| warn | warn(edge, strength?, opts?) | POST /api/warn/<edge> | 403 |
| fade | fade(rate?) | POST /api/fade | 200 |
| follow | follow(tag) | GET /api/follow?tag=<tag> | 200 |
Every row except fade was re-probed for this page. fade writes a decay
sweep across every path in the graph, so it was not fired against production
here; its 200 is the earlier probe’s result. Its route gates on the same
warn role action that mark and warn pass.
signal() is 401 because /api/signal/[...receiver].ts:67-74 requires the
Bearer token to byte-equal the server’s SERVER_SECRET — a server-to-server
value, not a developer key. api.one.ie does not serve that path either. There
is no external entry point for signal() today.
mark() and warn() reach the route and are then refused by an ownership
check: {"error":"forbidden","reason":"self_deal: cannot move an edge whose source you do not own"}. A malformed edge returns 400 with
title: "edge must be source→target" before the ownership check runs.
Two more methods a developer key cannot use: discover() returns 401
(/api/agents/discover) and highways() returns 403 (/api/export/highways).
harden is the sixth verb and has no client method. The only harden receiver in
the registry is chat:harden (src/receivers.ts:2466), called as
ask("chat:harden", …). client.know() is @deprecated and returns {};
learning:know is not in the registry.
Everything else on the surface is reachable through ask(). Of the 330
receivers, 310 declare effect: "ask" and 20 declare effect: "signal".
ask() and the outcome envelope
const cat = await one.ask("meta:catalog", {});
if (cat.kind === "result") {
console.log((cat.result as unknown[]).length);
}
Outcome<T> is a discriminated union of four kinds (src/types.ts:42-47),
intersected with three optional backpressure fields.
| Kind | Shape |
|---|---|
result | { kind: "result", result: T, latency: number } |
timeout | { kind: "timeout", timeout: true, latency: number } |
dissolved | { kind: "dissolved", dissolved: true, latency: number } |
failure | { kind: "failure", failure: true, latency: number } |
Backpressure fields, present on any kind: retryAfter (seconds), limit,
window (seconds).
The wire envelope is different from the client union. The HTTP route sends
{ outcome, signalId, receiver, … }; the SDK computes kind from which key is
present in the response body, not from the outcome field
(src/client.ts:229-233). See Known limits for where the two disagree.
signal() returns SignalResponse — { ok, routed, result?, latency, success, sui? } — not an Outcome.
Errors
Non-2xx responses throw. All classes extend SubstrateError, which carries
.status, .code, and .body.
| Status | Class | Code |
|---|---|---|
| 401, 403 | AuthError | auth_error |
| 400, 422 | ValidationError | validation_error |
| 429 | RateLimitError (.retryAfterMs) | rate_limit_error |
| 408, 504, client abort | TimeoutError | timeout_error |
| any other, 402 included | SubstrateError | — |
That table is the whole of throwForStatus (src/client.ts:67-74). It has no
402 branch, so a payment-required response reaches the caller as a bare
SubstrateError with .status === 402.
Three more classes are exported from @oneie/sdk/errors but are never
constructed anywhere in src/, so the client does not throw them:
DissolvedError (dissolved_error), InsufficientCreditsError
(insufficient_credits, status 402) and PaymentExceedsBudgetError
(payment_exceeds_budget, status 402, carrying .required and .max).
RateLimitError.retryAfterMs is likewise declared but never populated —
throwForStatus constructs it with the message alone.
Getting a key
curl -X POST https://one.ie/api/auth/agent \
-H 'Content-Type: application/json' -d '{}'
Returns { uid, apiKey, wallet, group }. No auth is required and the route is
rate-limited to 10 requests per minute per IP (RATE_LIMIT/RATE_WINDOW,
one.ie/web/src/pages/api/auth/agent.ts:24-25). wallet is null unless a Sui
address is supplied in the request body.
The key is permanent. createKey() inserts it with valid_to NULL —
“no forced expiry” — and the module docblock states it outright: “Agent keys are
permanent … No TTL expiry” (one.ie/web/src/lib/agent-key.ts:10-11,
:100-106). Verification treats a NULL valid_to as never-expiring
(one.ie/web/src/lib/api-auth.ts:334). Revocation is explicit: world:revoke-key runs UPDATE world_keys SET revoked_at = ? (one.ie/web/src/lib/world-receivers.ts:1086-1091). The route’s own docblock still says “24h key TTL”
(agent.ts:8); that line is stale, and the comment directly above the mint call
reads // Mint permanent API key (agent.ts:123-124).
There is no agent role. MEMBER_ROLES is owner, admin, member,
viewer, and packages/sdk/src/role-tiers.ts:23 says “agent/auditor are actor
attributes, not roles”. The route writes a world_actors row with
type: 'agent' and role: 'member' (agent-key.ts:94-95) and makes the new
actor owner of its own personal group (agent-actor.ts:68). At request time a
one--prefixed key resolves to role = "owner" scoped to group:<uid> and
nothing else — naming any other group is refused 403,
world-key cross-group access denied (api-auth.ts:350-355).
From the SDK this is client.authAgent(opts?), which posts to /api/auth/agent
directly. It is not ask("auth:agent", …) — that form returns 403
direct substrate access not permitted to an anonymous caller.
A fresh key on the free tier reads
{"tier":"free","api_limit":1000,"agent_limit":5} from
ask("dashboard:usage", {}).
The receiver registry
RECEIVERS (src/receivers.ts:177-5180) is the capability catalog. Counts are
from evaluating the object, and match meta:catalog in production.
| Measure | Count |
|---|---|
| Receivers | 330 |
| Namespaces | 78 |
effect: "ask" | 310 |
effect: "signal" | 20 |
Receivers with no auth label | 65 |
Distinct auth label strings | 29 |
Largest families:
| Namespace | Receivers |
|---|---|
world | 46 (45 prefixed world:, plus the bare world router) |
tasks | 24 |
workflow | 18 |
market | 16 |
video | 14 |
pages | 10 |
booking | 8 |
broadcast | 8 |
wallet | 7 |
foundation | 7 |
Three receiver names carry no family: prefix: world, notify, and
grant-capability. world is the universal router — it takes tags and returns
who took the signal.
await one.ask("world", { tags: ["hello"] });
// → { kind: "result", outcome: "result", receiver: "world",
// signalId: "…",
// result: { ok: true, routed: "ceo", tags: ["hello"] } }
The address form world:<tag-expr> carries tags in the receiver name instead.
Each entry declares five required fields — receiver, summary, request,
response, effect — and optionally auth, cost, reversible,
idempotent, settles, simulatable, roleAction, scope, version,
deprecated, surfaces, and examples (src/receivers.ts:61-142). auth is
optional, which is why 65 receivers carry none.
Typed access
ask and signal are conditional generics over ReceiverName. A declared
receiver infers its request type (ReqOf<R>) and response type (ResOf<R>)
from RECEIVERS with no codegen. A wrong payload to a known receiver is a
compile error. Any other string is the escape hatch, typed unknown.
import { RECEIVERS, RECIPES } from "@oneie/sdk/receivers";
import type { ReceiverName, ReqOf, ResOf } from "@oneie/sdk/receivers";
Object.keys(RECEIVERS).length; // 330
Exported types: Receiver<Req, Res>, ReceiverName, ReqOf<R>, ResOf<R>,
ReceiverCost ("free" | "variable" | { fixed: number }), Settlement
("none" | "offchain" | "onchain"), ReceiverEffect ("signal" | "ask"),
AuthLabel, RoleAction, RecipeName. receiver() is the declaration helper.
RECIPES holds six named ordered journeys: spine, build, trade,
transact, sop, a2a.
{ "spine": ["auth:agent", "world:create-key", "groups:join", "agents:sync"],
"build": ["world:create-workspace", "world:create-group",
"world:create-actor", "world:create-thing"] }
Runtime discovery
Three meta: receivers, in four call forms, let a caller read the surface
instead of a document. (meta: holds six receivers in all — meta:recall,
meta:reputation and meta:entitlements are not projections of the catalog.)
| Call | Returns |
|---|---|
ask("meta:catalog", {}) | 330 rows, each with receiver, summary, effect, plus whichever of auth, cost, reversible, settles, simulatable, idempotent the receiver declared (measured on the live response: idempotent 275, auth 265, cost 216, reversible 201, simulatable 30, settles 20) |
ask("meta:catalog", { goal }) | the recipe for one of the six goals |
ask("meta:schema", { receiver }) | { request, response } as JSON Schema draft 2020-12, generated from the zod contracts |
ask("meta:types", {}) | the workspace resource-type manifest plus built-in industry templates |
The first three run locally too, from @oneie/sdk/meta:
metaCatalog({ canCall }), metaRecipe(goal), metaSchema(receiver), plus
ownerOnly and the CatalogEntry type. meta:types has no local equivalent —
it reads the workspace, not the registry.
A malformed payload returns HTTP 400 with the field named:
{ "error": "validation", "receiver": "agent:own-link",
"field": "agent_id", "hint": "Invalid input", "expected": "string" }
SubstrateClient methods
70 methods on the prototype, grouped. Plus one static (fromApiKey) and four
instance members: pay (.accept, .request, .status), skills
(.import), subscribe (an alias of sub), and validateMode.
| Family | Count | Methods |
|---|---|---|
| Verbs | 7 | ask signal mark warn markDims fade follow |
| Identity | 4 | authAgent register signOut walletFor |
| Graph reads | 6 | groups actors things paths events learning |
| Groups and members | 9 | createGroup listGroups joinGroup leaveGroup groupMembers inviteMember changeRole removeMember provisionClient |
| Agents | 14 | listAgents publishAgent pullAgent unpublishAgent agentHistory rollbackAgent syncAgent emitAgentEvent discover commend flag status capabilities claw |
| Skills | 2 | listSkills unimportSkill |
| Market and money | 7 | hire bounty bounties publish listMarket payWeight revenue |
| Memory and learning | 10 | recall recallHypotheses rememberHypothesis reveal forget frontier highways trail select know |
| Observability | 6 | stats health usage signals state exportData |
| Other | 5 | chat fn createLink inbox sub |
The graph reads return async iterables and also expose .get().
Signatures worth stating exactly, because they are printed wrongly elsewhere:
| Method | Signature |
|---|---|
signal | signal(receiver, data?) — two arguments |
mark | mark(edge, strength = 1, opts?) |
warn | warn(edge, strength = 1, opts?) |
markDims | markDims(edge, { security, stability, simplicity, speed }) |
fade | fade(rate?) — one argument |
frontier | frontier(limit = 50) — a number |
highways | highways(limit = 50, from?) → HighwayEntry[] |
know | know() — deprecated, returns {} |
health() returns the live shape { status, agent, model, hasOpenRouter }.
Subpath exports
29 subpaths plus the root barrel. Each maps to one built module.
Client and transport
| Import | Exports |
|---|---|
@oneie/sdk/client | SubstrateClient |
@oneie/sdk/brain | BrainClient, BrainEnv |
@oneie/sdk/gateway | GatewayClient, GatewayEnv |
@oneie/sdk/fetch | oneFetch, createAgent, PaymentError, BudgetExceededError, X402NetworkError |
@oneie/sdk/urls | getApiUrl, getFrontendUrl, getEnvironment, resolveApiKey, resolveBaseUrl |
Contracts and types
| Import | Exports |
|---|---|
@oneie/sdk/receivers | RECEIVERS, RECIPES, receiver(), the receiver types |
@oneie/sdk/types | Outcome, SignalResponse, SdkConfig, request/response interfaces |
@oneie/sdk/schemas | zod schemas — SignalResponseSchema, HealthSchema, AgentDefinitionSchema, WorkflowDiffSchema, and others |
@oneie/sdk/meta | metaCatalog, metaRecipe, metaSchema, ownerOnly, CatalogEntry |
@oneie/sdk/openapi | buildReceiverPayload, generatedRegion, injectRegion |
@oneie/sdk/errors | the eight error classes |
Authority
| Import | Exports |
|---|---|
@oneie/sdk/role-tiers | MEMBER_ROLES, rungOf, tierOf, minTier |
@oneie/sdk/role-actions | ROLE_ACTIONS, isRoleAction |
@oneie/sdk/receiver-action | roleActionFor |
Identity and session
| Import | Exports |
|---|---|
@oneie/sdk/storage | getToken, setToken, clearToken |
@oneie/sdk/wallet | generateWallet, recoverWallet, deriveWalletFromSeed, generateAgentKeypairs, suiAddressFromPubkey, evmAddressFromPubkey — see Wallets and custody |
@oneie/sdk/launch | launchToken, LaunchOpts, LaunchResult |
Domain
| Import | Exports |
|---|---|
@oneie/sdk/market | Deal, Rubric, Quote, quoteBlend, affiliateCommission, advance, passesBar, InvalidTransition |
@oneie/sdk/billing | CREDITS_PER_USD, USD_PER_CREDIT, creditsForUsd, creditsForCents, rateFor, computeBurn |
@oneie/sdk/work-contract | GRAMMAR, validateContract, hydrateScope, Verdict, Scope |
@oneie/sdk/blocks | normalizeBlockProps, describeFields, variantsWithFields, CHROME_PROPS |
@oneie/sdk/compile | parseAgentMd, compileAgent, priceToAccepts, ParsedAgent |
Templates and generated
| Import | Exports |
|---|---|
@oneie/sdk/templates | TEMPLATES, TemplateName, manifestToTql |
@oneie/sdk/fn-allowlist | FN_ALLOWLIST, isAllowedFn — a readonly tuple, order is load-bearing |
@oneie/sdk/generated/fn-map | FN_MAP — 270 generated schema functions — and FN_MAP_VERSION |
@oneie/sdk/generated/skins/engineering | asEpic, asStory, asBug, asChore, asSpike, asSprint |
Other
| Import | Exports |
|---|---|
@oneie/sdk/telemetry | emit, isDisabled, getSessionId, emitEvolution |
@oneie/sdk/testing | createMockSubstrate |
@oneie/sdk/handoff | validateEthAddress, generateDeployLink |
Some symbols exist only on the root barrel and have no subpath:
createSdkAuthClient, trail, tagFingerprint, pluginBuild,
pluginBuildEnterprise, readWorkspaceSoul, buildSoulSuffix,
tqlToManifest, BASE_MANIFEST, SDK_VERSION, and everything from pay.ts,
skills.ts, and broadcast.ts.
Environments
| Runtime | Import style |
|---|---|
| Node | root barrel or any subpath |
| Bun | root barrel or any subpath |
| Cloudflare Workers | subpaths only |
In a Cloudflare Worker, import by subpath — @oneie/sdk/brain,
@oneie/sdk/fn-allowlist, @oneie/sdk/role-actions. Do not import the bare
barrel. The barrel pulls in modules that generate randomness at global scope,
which a Worker isolate refuses outside a request handler; the isolate fails to
initialise and the request returns 500. This is why fn-allowlist,
role-actions and receiver-action are subpath-only and are deliberately not
re-exported from src/index.ts.
The barrel-only symbols listed above are therefore Node and Bun only. trail
and tagFingerprint in particular have no subpath and cannot be imported from a
Worker.
The package is ESM only. There is no CommonJS build, so require("@oneie/sdk")
does not work.
Known limits
signal(), mark() and warn() have no external door. Measured 2026-09-06
with a valid agent key: 401, 403, 403. discover() is 401 and highways() is
403. signal() requires SERVER_SECRET, which is not issued to developers, and
api.one.ie does not serve /api/signal/* either.
outcome.kind is derived from key presence, not from the server’s outcome
field. src/client.ts:229-233 computes kind as "result" in raw ? "result" : "timeout" in raw ? "timeout" : "dissolved" in raw ? "dissolved" : "failure".
Calling an unknown receiver returns the wire body
{"outcome":"dissolved","signalId":…,"reason":"unknown_receiver"}, which has no
dissolved key — so the SDK hands the caller kind: "failure" for a response
the server labelled dissolved. Read the raw outcome field when the distinction
matters.
A self-minted agent key is permanent and scoped to one group.
POST /api/auth/agent mints a non-expiring key (valid_to NULL) whose only
kill is explicit revocation. It carries owner authority over its own
group:<uid> and 403s on every other group, so it cannot read or write a
workspace you already own — mint a scoped key with world:create-key for that.
Treat the minted string as a standing credential, not a session token.
auth labels do not reliably bind. 65 of the 330 receivers declare no
auth label; the other 265 spread across 29 label strings that mix caller
classes (public, session, agent_key) with action names (manage_clients,
read_corpus, mint_capability). A receiver’s declared label is not a
guarantee that a call will be refused — a key self-minted anonymously from
POST /api/auth/agent was observed completing a receiver labelled
manage_clients.
client.know() is dead. It is @deprecated and returns {}. There is no
learning:know receiver.
The npm README has drifted. packages/sdk/README.md is not in the
typechecked-snippet allowlist (packages/sdk/tests/docs-snippets.test.ts:21-30)
and its examples do not compile against the shipped SDK: a three-argument
signal(), a dims-object mark(), a two-argument fade(), a string-argument
frontier(), a { highways } destructure, know() shown as live,
ONEIE_API_URL documented as defaulting to https://api.one.ie, and every
ask() example calling tutor:explain, which is not a receiver. Use the
signatures on this page.
Two in-repo reference docs carry stale counts.
text/receivers-reference.md:58 says 120 receivers; text/sdk-reference.md
says 260+ at line 22 and pins version 0.14.8 in its frontmatter (line 4). The current numbers are 330 and 0.14.13.
text/signals-catalog.md is the one with a parity guard.