InlayDocs
Reference

@inlayai/sdk

The core client — InlayChatClient, transports, errors, and the row/timeline helpers.

The core client: the wasm-backed chat driver, the HTTP transport, the error taxonomy, and the row/timeline helpers. Framework-free — the React package is a thin reactive layer over this.

Every entry point takes the same ConnectionOptions bag: a session, with baseUrl defaulting to production (INLAY_PRODUCTION_URL) and a transport field as the advanced seam (tests, non-browser hosts).

The client

createChat

createChat(opts: CreateChatOptions): InlayChatClient

InlayChatClient

One conversation against a deployed agent. The wasm reducer owns parse + reduce and returns effects as data; the client is the driver — it opens and pumps streams, runs tool handlers, delivers results, aborts, and cancels. Construction warms the wasm module in the background; the first send() joins the same init promise.

CreateChatOptions

CreateChatOptions extends ConnectionOptions{ agentId, session } is the whole happy path.

Prop

Type

Getters

GetterTypeNotes
snapshotChatSnapshotthe FLAT top-level transcript (inner-subgraph rows dropped); no composite containers
fullSnapshotChatSnapshotthe FULL path-tagged transcript (backs per-node joins)
runRunSnapshotthe topology projection
streamingbooleanan un-aborted run is in flight
canSendbooleanstatus map AND the synchronous latches — a gated composer never meets a rejection
canCancelbooleansomething live to stop (streaming, or the send/reopen startup windows)
canRetrybooleanfailed + retryable, nothing in flight

Methods

MethodSemantics
send(text, opts?: { throwOnFailure?: boolean })Starts or resumes a turn; resolves when the stream closes. Rejects InlayRunError on failure by default (headless is reject-by-default, unlike React); throwOnFailure: false relaxes to resolve. Misuse (a turn/reopen in flight, status awaitingTool) rejects in every mode — promise rejections, not sync throws.
retry()Re-drives the failed node (POST …/retry); rejects like send on re-failure (the conversation stays retryable — the cursor survives).
cancel()Stops the turn (streaming or paused); a no-op when idle.
reset()Abandons the conversation to idle/empty (synchronous); the next send starts fresh.
resume(id, opts?)Reopens a persisted conversation (hydrate); opts pages the window. Idempotent; refuses a foreign-agent id unless { allowForeignAgent: true } — then READ-ONLY (the transcript hydrates; send/retry/submitToolResult refuse, canSend/canRetry read false — the multi-agent-sidebar affordance; loadEarlier pages, delete tombstones). A same-id resume while streaming is a no-op; a superseded attempt's failure resolves silently (the newest attempt reports its own truth).
submitToolResult(toolCallId, content)Delivers a durable tool result and resumes. A no-op for an unknown id; rejects while a reopen/page is in flight.
loadEarlier(limit?)Pages the reopened log backward; resolves to the number of rows added (0 at the start). Rejects while streaming or without a prior resume.

Transport

Transport

The HTTP surface the client drives — an interface so tests can inject a mock and non-browser hosts can swap fetch.

MethodEndpointNotes
openExecuteStream(agentId, body, signal?)POST /v1/agents/:id/execute/streamfresh run, returns the SSE stream
openResumeStream(conversationId, content, signal?)POST /v1/conversations/:id/messagescontinue with a user message
openRetryStream(conversationId, signal?)POST /v1/conversations/:id/retryno body; returns the resumed continuation
postToolResult(conversationId, toolCallId, content, signal?)POST …/tool-resultsdelivers a result AND resumes — returns a stream, not a bare POST
cancel(conversationId)DELETE /v1/conversations/:id404 is the API's masked "nothing to cancel" — not an error
fetchConversation?(conversationId, opts?)GET /v1/conversations/:idraw JSON for the wasm hydrate; optional — without it resume/loadEarlier throw a clear error
listConversations?(query?)GET /v1/conversations/mineparsed summaries; optional — without it listConversations throws
deleteConversation?(conversationId)POST /v1/conversations/:id/deletesoft-delete from history; optional — without it deleteConversation throws. 404 (already gone) tolerated

HttpTransport

Implements Transport over fetch: resolves the bearer token from session, refreshes and retries ONCE on a 401 (provider-backed sessions only), and retries transient network rejections with exponential backoff (HTTP error responses are never retried).

HttpTransportOptions

Prop

Type

ConnectionOptions

The connection bag every entry point accepts. Just session in the common case — the API origin defaults to production. A given transport wins over session/baseUrl (the test seam).

Prop

Type

INLAY_PRODUCTION_URL

"https://app.inlayai.com" — the default baseUrl of every entry point in all three packages. Override only for a self-hosted or staging deployment.

History — listConversations

listConversations(conn: ConnectionOptions, query?: ConversationListQuery): Promise<ConversationSummary[]> — a thin stateless wrapper (throws if the transport doesn't implement it). The snake→camel mapping of ConversationSummary happens here — the one REST DTO the client consumes; everything else JS-facing crosses the wasm boundary already camel.

deleteConversation(conn: ConnectionOptions, conversationId: string): Promise<void> — remove one conversation from the caller's history (a soft-delete tombstone server-side). Idempotent — deleting an already-gone conversation is not an error. In React, prefer useInlayHistory's deleteConversation(id) (it also drops the row from the list optimistically). Throws a clear error if the transport omits deleteConversation.

Built-in tool names

The platform's INTRINSIC tools reach the client as timeline tool-call rows carrying a toolName (with requiresClientResponse: false — observability only). Client code that special-cases a built-in — rendering a code.exec call, reacting to a memory.remember — should reference these constants instead of magic strings. Agent-DECLARED client tools are typed per-agent by inlay codegen (<Base>ToolName = keyof <Base>ToolHandlers); these are the platform intrinsics any agent might invoke. They're pinned to the Rust source of truth by a test, so a rename on either side fails the build.

ConstantWire name
MEMORY_TOOLobject — PEEK, LOAD, REMEMBER, UNLOAD, FORGET, LIST, APPEND, REPLACE, LINK, UNLINK, RESURFACE, RECALLmemory.<op>
CODE_EXEC_TOOL"code.exec"
SCRATCHPAD_TOOL"scratchpad"
RETRIEVAL_SEARCH_TOOL"retrieval.search"
WEB_SEARCH_TOOL"web.search" (only when a search key is configured)
URL_FETCH_TOOL"url.fetch"
ARTIFACT_EDIT_TOOL"artifact.edit"

The dynamic prefixes (agent.<alias>, mcp.*) are per-tenant/per-alias, not fixed names, so they're deliberately not constants here.

Capabilities

Capabilities

capabilities(status: ChatStatus): Capabilities — the pure (status → allowed) map, so apps drive buttons off semantics instead of re-deriving !streaming math (which gets awaitingInput wrong). For canSend this is the status HALF only — InlayChatClient.canSend ANDs the synchronous latches, and is the only safe composer gate.

canSend = status ∉ awaitingTool — note the terminal statuses allow it, where send starts a FRESH conversation. canCancel = a live run (streaming, plus the synchronous starting/reopen windows the status map can't see). An idle row is continuable, not stoppable — delete() is the permanent end. reset has no flag — valid in every status.

Prop

Type

Errors

Every error the SDK raises derives from InlayError.

InlayError

The base class for every SDK error.

InlayRunError

{ code: InlayErrorCode; reason?: PublicError } — a run failed (code: "run_failed") or the stream died opening/pumping (code: "open_failed"). Deliberately ONE rejection type, so try { await send() } catch catches the network and the agent. reason is the customer-safe cause (absent when no node reported one). Match on code, never the message.

InlayErrorCode

"run_failed" | "open_failed".

InlayHttpError

{ status: number; body: string } — a non-2xx API response. Getters: isAuth (401), isForbidden (403), isNotFound (404), isConflict (409), isQuotaExceeded (429 billing denial — do not auto-retry; render your own upgrade CTA), isRateLimited (429 transient throttle — honor Retry-After), and quota (the parsed figures, below).

QuotaDetails

{ limitMicros?, usedMicros?, limitTokens?, usedTokens?, resetsAt? } — the structured extension members of a quota_exceeded 429, parsed from the problem body (resetsAt as a Date). null from the quota getter for any other error or a pre-7A server — version tolerance by construction. Fields are present by quota kind: cost denials carry the micros pair, token denials the tokens pair. resetsAt is the absolute moment the billing period opens, not a retry delay.

Billing (end-user reads)

Typed reads and hosted payment flows over /v1/billing/*, scoped by the session JWT. Your app renders its own pricing page, wallet, and upgrade CTA from these figures. These are your users' billing data, not Inlay's commercial pricing. See the billing guide.

BillingFacade

Also reachable as chatClient.billing.

MethodReturns and behavior
balance()Promise<BillingBalance>: the credit wallet decomposition.
usage()Promise<BillingUsage>: current-period usage and allowance, using the gate's server-side counter figures.
plans()Promise<BillingPlans>: the tenant's published catalog and currentPlanId.
invoices()Promise<BillingInvoice[]>: the end user's own invoices, newest first.
topup(amountUsd)Promise<HostedCheckout>: a hosted invoice URL for a credit top-up. The argument is USD, not micro-USD; credits arrive after payment via webhook.
subscribeCheckout({ planId, successUrl, cancelUrl })Promise<HostedCheckout>: hosted checkout for a new subscription to a published plan.

Both mutations throw if the transport returns no hosted URL. Redirect to the returned URL; do not collect card data yourself. They require a connected Stripe account with charges enabled; unconfigured accounts receive 409. Subscribe checkout is for new subscriptions only, not plan changes for an already-subscribed user. See hosted subscribe and top-up.

createBilling

createBilling(conn: ConnectionOptions): BillingFacade — construct the facade standalone (a pricing page doesn't need a chat client): { session } suffices.

Retrieval (built-in RAG)

The retrieval surface (the built-in-RAG feature): ingest + query + aggregate

  • the index profile + the eval pack, against the /v1/retrieval/indexes/:ns/* routes. The ACL + per-user-namespace enforcement is server-side — the client never names the reserved acl field and never passes a principal.

RetrievalFacade

The retrieval client. Construct via createRetrieval({ session }).

MethodReturns
upsertDocuments(ns, documents)Promise<IngestStats> — ingest a batch (management tier)
deleteDocuments(ns, sources)Promise<number> — delete every chunk of the given sources
query(ns, text, opts?)Promise<RetrievalHit[]> — top-k with filters/include/diversify
queryBatch(ns, queries)Promise<RetrievalHit[][]> — ≤16 subqueries; the atomic reserve-N 429 contract
aggregate(ns, aggregateBy, groupBy, filters?)Promise<AggregateRow[]> — a GROUP BY over the declared metadata
getProfile(ns)Promise<ProfileInfo> — the effective profile + the legacy flag
putProfile(ns, profile, opts?)Promise<ProfileUpdate> — the evolution classification + status
previewProfile(ns, profile)Promise<ProfilePreview> — the evolution a put WOULD produce, without applying
getEvalPack(ns)Promise<EvalPack | null> — the golden set, if any
putEvalPack(ns, pack)Promise<void> — install the golden set for that index's corpus-publication gate, not every agent publish
evaluate(ns)Promise<EvalReport> — run the pack against the live index
stats(ns)Promise<RetrievalStats> — chunk counts, dim, the pinned status

createRetrieval

createRetrieval(conn: ConnectionOptions): RetrievalFacade — construct the retrieval facade standalone.

Operator triggers (schedules + run-as)

The Phase-2.5 surface: fire agent runs SERVER-SIDE, with no end-user browser session — cron-scheduled runs (the schedules methods) and the on-demand runAs. Both run the agent AS a named end user (their durable memory, usage, and recall scope to them — the AuthContext::System principal). These are MANAGEMENT-tier calls — an operator api-key / dashboard credential, never a customer session. Pass the operator credential as the session: createOps({ session: process.env.INLAY_SECRET_KEY! }).

OpsFacade

The operator-trigger client. Construct via createOps({ session }).

MethodReturns
createSchedule(agentId, input)Promise<Schedule> — a cron schedule (its first nextRunAt is computed). input.version pins it to an immutable agent version
listSchedules(agentId)Promise<Schedule[]> — an agent's schedules, oldest-first
getSchedule(agentId, scheduleId)Promise<Schedule> — one schedule
setScheduleEnabled(agentId, scheduleId, enabled)Promise<Schedule> — pause/resume
updateSchedule(agentId, scheduleId, input)Promise<Schedule> — edit the config (cron/timezone/task/enabled + the version pin). Only the present fields change; a cron change recomputes nextRunAt; version is a tri-state (absent/null/number)
deleteSchedule(agentId, scheduleId)Promise<void> — delete
listTenantSchedules(opts?)Promise<Schedule[]> — the tenant-wide list. ADMIN-only: spans every act-as userId + task; never render to end users. opts.limit/opts.offset page it (default 50, max 200)
listMySchedules(opts?)Promise<MySchedule[]> — the end-user-safe list (only the session user's own) — safe to render to end users. opts.limit/opts.offset page it
runAs(agentId, { userId, task?, version? })Promise<RunAsResult> — fire a run AS the named user (sync, a fresh conversation). version pins it

createOps

createOps(conn: ConnectionOptions): OpsFacade — construct the facade standalone.

Schedule

One schedule: cron + timezone, the run task (the kickoff instruction — seeded as the run's first user message), the act-as userId, enabled, the computed nextRunAt / lastRunAt, the consecutiveFailures circuit-breaker count (auto-disables past 5), and the optional pinnedVersion (absent = tracks latest-published; a number = pinned to that immutable agent version, so a publish/rollback doesn't move the schedule).

MySchedule

The END-USER-SAFE schedule view (the listMySchedules result) — a whitelist over the internal schedule: id, agentId, cron, timezone, pinnedVersion, enabled, nextRunAt / lastRunAt, consecutiveFailures, createdAt. It has NO task (the kickoff) and no lease/tenant bookkeeping — safe to render to end users.

CreateScheduleInput

createSchedule's input: cron (standard 5-field), optional timezone (default UTC), the task kickoff, the act-as userId, optional enabled, optional version (pin to an immutable agent version; validated to exist).

UpdateScheduleInput

updateSchedule's input — a partial config edit (only the present fields change): cron, timezone, task, enabled, and version as a tri-state (absent = unchanged, null = clear the pin, a number = pin). A cron/ timezone change recomputes the next fire time. agentId/userId aren't editable.

RunAsResult

The fired run's conversation: conversationId, status, optional output.

RetrievalDocument

One document to ingest: text + optional id, kind (doc | row), source, heading, metadata (validated against the index's profile).

RetrievalHit

One retrieval hit. scoreKind marks what distance carries (l2 / sq8 / rrf / rerank). generation is the corpus marker for citation drift detection. highlights are matched-fragment byte offsets into text (when the query asked for them via include: ["highlights"]).

RetrievalQueryOptions

Query shaping: k, nprobe, contextChars, kind, exact, filters (the positional filter tree), include (["highlights"]).

RetrievalProfile

The index's declared profile (the closed schema, rag.md §4): version, embedder, plus optional chunker, contextual_prefix, ivf, metadata, retrieval, answer_policy, management_acl.

The retrieval field holds the query-time knobs: bm25 (the lexical half), fusion (the hybrid mode + weights), rerank ({ kind: "none" | "api", model? } — a reranker pass over the fused candidates), diversify ({ attribute, limit } — an MMR-style spread over a declared metadata field).

EvalPack

The eval pack: cases (golden queries + expectSources / expectNone) + minRecall + maxFalseHitRate — the thresholds that index's corpus-publication gate enforces when a pack is installed. This is not an agent-publish eval.

EvalReport

The eval report: total, passed, recall, falseHitRate, gatePassed, and per-case results.

Supporting types

IngestStats (the ingest result), AggregateSpec (["Count"] / ["Sum", field]), AggregateRow, EvalCase, EvalCaseResult, RetrievalStats, ProfileInfo, ProfileUpdate, ProfilePreview, BatchResults — see the facade method signatures above.

Citations (grounded answers)

A grounded answer's citations (rag.md §5.4) ride the message.end payload into the assistant row's citations. A citation is a point-in-time snapshotgeneration + snippet let a frontend detect corpus drift after a re-publish (chunk ids renumber).

Citation

One claim's backing passage: claim, chunkId, source, snippet, plus optional heading + generation.

Helpers

FunctionReturns
citationsOf(row)the row's citations ([] for a non-grounded turn)
allCitations(rows)every row's citations, in order
citationDrifted(citation, currentGeneration)whether the corpus re-published since the answer
partitionByDrift(citations, currentGeneration){ current, drifted }
citationMarkers(citations, currentGeneration?)CitationMarker[] — the 1-based [n] footnote markers + the drift flag

CitationMarker

A renderable footnote marker: marker (the 1-based [n]), citation, and drifted.

Row guards and accessors

All take Row and are re-exported from @inlayai/react (one front door).

FunctionReturns
isUserRow / isAssistantRow / isOutputRow / isToolCallRow / isToolResultRowtype guards (row is …)
hasReasoning(row)row is AssistantRow & { reasoning: string } — narrows the text non-optional
rowText(row?)user/assistant content, else "" (undefined-safe)
rowReasoning(row?)assistant reasoning, else ""
rowComplete(row)false only for streaming assistant/output rows — append-only kinds are complete on arrival
transcript(rows)top-level (UserRow | AssistantRow)[] — the flat chat view (pinned equal to the index's)
outputsNamed(rows, name)the output rows for one name, oldest first — the per-turn production history bindings can't answer

The timeline index

Indexes over the ordered row array, built ONCE per commit with reference-preservation: a slice whose contents didn't change keeps its object identity, so a useSyncExternalStore consumer keyed on one path re-renders only for that path. Build it at the commit (as the store does) — building one per render is the exact anti-pattern it exists to prevent.

buildTimelineIndex

buildTimelineIndex(rows, previous?): TimelineIndex

deriveFeed

deriveFeed(rows, run, config, previous, hasEarlier?): FeedRow[] composites the full row array into FanOutRow, SubagentRow, and RepeatRow containers per the static config, threading previous for reference stability. hasEarlier takes the snapshot's windowed-history flag. Passing null as config returns the supplied rows flat, including inner rows. This helper does not check the conversation's agent or version; the generated React path performs that guard before composition. Pacing and reference threading remain client-side. Plumbing, not the front door: the generated hook calls it for you via useInlayChat's machinery. The headless client's snapshot.rows and raw useInlayChat without generated graph config stay flat.

computeFeedConfig

computeFeedConfig(nodes, regions, subagentPaths): FeedConfig normalizes the generated module's NODES/REGIONS/SUBAGENTS into the feed's static inputs. regions is a required readonly FeedRegion[], not an edge table. Node kind and ensemble samples supply repeat metadata; subagent paths supply containers. The generated hook supplies these automatically, and the machinery memoizes one per generated config; apps using codegen do not configure feeds manually.

There is no SDK graph prover. Descriptors are authoritative, including [], which means no fan-out grouping even if NODES describes a Collect router. Missing region metadata in an older generated module also does not trigger inference from NODES/EDGES; regenerate the module. EDGES remains available for selectBranches.

FeedConfig

The normalized static inputs returned by computeFeedConfig:

MemberMeaning
regionsFeedRegion[]: a copy of the authoritative descriptor list; normalization does not discover or re-prove regions.
subgraphsThe absolute paths of the graph's subgraph nodes.
repeatablesA map keyed by absolute node path (/-joined). An ensemble has mode: "parallel" and optional total from its generated samples (the static sample count); a for_each_llm has mode: "sequential" and no static total.

Repeat-only graphs still populate repeatables and use the feed pipeline, even when regions and subgraphs are empty. See RepeatRow for the resulting row's grouping and progress semantics.

FeedRegion

One authoritative fan-out descriptor in the generated REGIONS export:

Prop

Type

source, join, and each branchEntries entry are absolute node-id paths. branchEntries follows branch declaration order; branchNodes is parallel to it, with each branch represented by sorted slash-joined absolute path strings, excluding the selected join. A direct branch to the join has an empty membership list, not an omitted branch.

Generated descriptors always include fromPort: a string for a single-port fan-out, or null for a Collect group across rule ports. else and error fallback targets are not parallel tracks. The projection keeps the shared analyzer's selected join and declines ambiguous fallback ingress; it does not find a different join for the rule-only subset. This is static presentation metadata, not runtime firing provenance or an execution-policy declaration.

TimelineIndex

Prop

Type

FunctionReturns
rowPath(row)the row's absolute path, /-joined ("" = top level)
rowsAtPath(index, path)the indexed slice (reference-stable; shared empty on miss)
rowsUnder(index, path)rows at OR BELOW a path — the container rollup
latestAssistantAt(index, path)newest assistant row at a path (subscribes to the whole index — prefer latestAssistantIn)
latestAssistantIn(rows)newest assistant row in an already-scoped slice
assistantsInActivation(rows)ALL assistant rows of the current activation (splits from artifacts and server tool loops stay joined)

Validation, wasm, escape hatches

validateArgs(value, schema): string | null — the tolerant JSON-Schema subset validator (unknown constructs pass) used for tool args at the client boundary and for settled binding values.

ensureWasmReady(input?: WasmInput): Promise<void> — idempotent wasm init (Node reads from disk; the browser fetches relative to the module).

WasmInput: BufferSource | URL | Response | WebAssembly.Module — pass it when your bundler can't resolve the default .wasm URL.

StreamPacer — the wasm reveal pacer the smoothing hooks share, exported as an escape hatch. Almost nothing should touch it directly.

On this page