@moreflow/api
v0.2.0
Published
Generated, typed ESM client for the Moreflow public operations API.
Maintainers
Readme
@moreflow/api
The generated, typed ESM client for moreflow's public operations API. The client includes every canonical public operation, including the credential management operations selected by the admin scope. It derives input, output, stability, idempotency, pagination, and error types from the reviewed contract snapshots, and each method's editor documentation comes from the operation's registry description, examples, and declared errors.
- API guide: credentials, scopes, errors, idempotency, and the design standard.
- Quickstart
- OpenAPI document
- Contract changelog
- SDK overview: the browser SDKs for forms, booking, chatbots, and tracking, and the agent plugins.
SERVER-ONLY: use this package in a trusted server runtime. Never ship the API token or OAuth access token in browser code, a client bundle, or page source. Use the public browser SDKs for embeds.
Quickstart
import { createMoreflowApiClient } from '@moreflow/api';
const moreflow = createMoreflowApiClient({
// A static bearer token or an OAuth access token is sent as Bearer auth.
token: process.env.MOREFLOW_API_TOKEN
});
const clients = await moreflow.clientsList({ query: 'Weber', limit: 25 });
const created = await moreflow.clientsCreate({ firstName: 'Ada', lastName: 'Lovelace' });
for await (const client of moreflow.iterate('clients.list', { status: 'lead' })) {
console.log(client.id);
}Successful calls return the operation output directly. For example,
clientsList() returns { items, nextCursor } at the top level; the API does
not add an { success: true, data } envelope. Each operation is also available
through the generic invoke(operationId, input, options) method.
Credentials and scopes
Create an account-bound integration token on the
API token settings page
(Settings → Integrations → API tokens). Choose a read or write scope bundle,
review its operation picker, and create the token. A token grants exact
operation ids, never prefixes or wildcards; the bundle is resolved to those ids
when the token is issued, so changing a bundle later never changes an existing
token. Discover what a token may call with operationsList() and
operationsDescribe({ id }).
The token selects the account: no method accepts accountId.
X-Moreflow-Account-Id may select another authorized membership through the
client headers option. Headless agents can mint narrower tokens with
apiTokensAgentCreate() from a credential that holds the admin:tokens bundle;
scopes never widen. Hosted clients can use OAuth 2.1 with PKCE instead and pass
the access token.
Pass either credential through token; it is sent as an HTTP
Authorization: Bearer token. Pass fetchImpl to use a custom runtime or a
local test server. These are the only credential and fetch option names.
Environments
The client calls production, https://api.moreflow.io, by default. Pass
baseUrl for another environment:
const development = createMoreflowApiClient({
baseUrl: 'https://dev-api.moreflow.io',
token: process.env.MOREFLOW_DEV_API_TOKEN
});baseUrl: '' produces relative /v1/operations/... URLs, consistent with the
browser SDKs. A server has no page origin: use this only with a custom
fetchImpl that resolves relative URLs to a trusted origin. The default Node
fetch needs an absolute base URL.
Errors and recovery
Failed calls throw MoreflowApiError:
import { MoreflowApiError } from '@moreflow/api';
try {
await moreflow.dealsUpdate({ dealId, expectedVersion: 3, amountCents: 520000 });
} catch (error) {
if (!(error instanceof MoreflowApiError)) throw error;
error.status; // HTTP status, or undefined when no response arrived
error.category; // ApiErrorCategory, e.g. 'ABORTED'
error.code; // the operation's declared codes, e.g. 'VERSION_CONFLICT'
error.requestId; // quote it when contacting support
error.declaredRecovery; // { operation: 'deals.get', args } — the next call to make
error.retryable; // whether the same request may succeed later
}error.error is the typed error body of that operation:
{ category, code, message, recovery, details, requestId }. recovery names
the operation (and arguments) that resolves the failure; declaredRecovery
falls back to the recovery the contract declares for the code when the server
sent none. Every operation's declared errors are in
publicApiOperationMetadata[operationId].errors.
isMoreflowError(error), exported by this package, recognizes the shared Symbol.for('moreflow.error') brand across separately bundled @moreflow/forms, @moreflow/chatbots, @moreflow/meetings and @moreflow/api. It narrows a caught value to the common error shape below. Use instanceof MoreflowApiError when you also need API-specific fields such as operationId, category, error, response, declaredRecovery or retryAfterSeconds.
| Field | Type | Meaning |
| --- | --- | --- |
| code | string | Stable failure code; branch on this, including newer server codes. |
| message | string | Developer explanation, using the server's message when available. |
| status | number \| undefined | HTTP status; undefined when no response arrived. |
| retryable | boolean | Whether the same request may succeed later: HTTP 408, 425, 429, 5xx, network failures or a server-declared retryable failure. Caller cancellation (ABORTED) is not retryable. |
| recovery | { operation, args?, requiredFields? } \| undefined | The operation that resolves the failure, its known arguments and input still needed. |
| details | Record<string, unknown> | Structured server context; {} when absent. Operation context is merged into it. |
| requestId | string \| undefined | Server request id to quote when contacting support. |
| responseBody | unknown | Raw response body, unchanged. |
When no structured error body was received, the client reports NETWORK_ERROR, REQUEST_TIMEOUT, ABORTED (caller cancellation), or REQUEST_FAILED (an HTTP error without a code). REQUEST_TIMEOUT is also retryable.
Retries and timeouts
The client retries up to maxRetries times (default 2) after HTTP 408, 425, 429, 5xx,
IDEMPOTENCY_IN_PROGRESS, a timeout, a network failure, or a server-declared retryable failure. It waits for
Retry-After when the API sends it, otherwise for an exponential backoff with
jitter; a requested wait above 60 seconds is returned to you as the error
instead. Queries and writes that require an idempotency key are retried with
the same key, so the API replays a completed write instead of running it again. The few writes without a
required key are retried only after a 429, which the API answers before running
the operation.
timeoutMs bounds each attempt; there is no default timeout. A signal you
pass cancels the request and any pending retry delay. All three can be set on
the client and overridden per call:
const moreflow = createMoreflowApiClient({ token, maxRetries: 3, timeoutMs: 30_000 });
await moreflow.clientsList({}, { maxRetries: 0, timeoutMs: 5_000, signal });Idempotency
Operations with publicApiOperationMetadata[operationId].idempotencyRequired
need an idempotency key. The client generates one per call when you pass none
and sends it as the Idempotency-Key header. Pass your own key when a retry
from your side (after a crash, or from a queue) must replay the stored result
instead of repeating the write:
await moreflow.dealsUpdate(
{ dealId, expectedVersion: 3, amountCents: 520000 },
{ idempotencyKey: `deal-update:${dealId}:3` }
);Reusing a key with a different request returns IDEMPOTENCY_CONFLICT. The key
is sent only as a header; it stays in the JSON body only when the operation's
input schema declares an idempotencyKey field. withIdempotency(input, key)
still works but is deprecated in favour of the idempotencyKey request option.
Pagination
List operations return { items, nextCursor }. Pass nextCursor back as
cursor for the next page, or let the client follow it:
for await (const page of moreflow.iteratePages('clients.list', { limit: 100 })) {
console.log(page.items.length);
}
for await (const note of moreflow.iterate('clients.notes.list', { clientId })) {
console.log(note.id);
}iterate and iteratePages require the input when the operation has required
fields (here clientId). Cursors are opaque; a malformed or expired cursor
returns INVALID_CURSOR.
Confirmation
Some writes declare that a person must approve them. Set confirm: true only
after explicit user confirmation, when a prior call returned
CONFIRMATION_REQUIRED.
Issues and contributing
Bug reports and feature requests are welcome in this repository's issue tracker. The published source is generated in the moreflow monorepo from the public operation registry and its committed contract snapshots.
Stable v1
The client is generated from the v1 contract freeze. Integrations use
/v1/operations/<operationId>. Each entry in publicApiOperationMetadata
publishes stability and contract version. Stable operations have a 180-day
deprecation window; beta operations have 30 days.
