Use the live contract
This guide explains the workflow, not a copied operation catalog. Start with the authenticated operations.discovery.list and operations.discovery.get operations, then use the live OpenAPI document for the exact fields, validation, examples, error codes, and agent-safety metadata available to your account. The OpenAPI agent annotations derive from each operation’s agent.effect and its declared confirmation and reversibility.
resource.action (for example clients.list). Integration tokens grant exact operation ids, never prefixes or wildcards.Canonical public operation ids use module.resource[.subresource].verb. New ids end in list, detail, create, update, or delete. Lists return { items, nextCursor }; detail reads return one resource; mutations use the matching write scope and the standard success/error envelope plus idempotency rules. Existing documented grandfathered ids remain authoritative when discovered.
Contract lifecycle and release changes
The public contract is versioned per operation. Read the API consumer changelog before upgrading an integration; it is generated from the same committed snapshots that drive the live schemas and the @moreflow/api release notes.
For the euro AI allowance migration, read the permanent billing.entitlements.get v4 notice below, including the replacement metric and its units.
billing.entitlements.get v4: euro AI allowance / AI-Kontingent in Euro
English
billing.entitlements.get now uses contract v4. Replace ai_requests and ai_credits with ai_usage in the request’s usage selector and read the response at usage.ai_usage. The retired metric names are no longer accepted.
AI amounts are integer micro-euros: 1 EUR = 1,000,000 micro-euros. When present, usage.ai_usage.limit is the effective allowance for the usage period, including purchased top-ups; usage.ai_usage.current is usage so far; and usage.ai_usage.remaining is the available allowance. Divide numeric values by 1,000,000 to display euros. These values are neither request counts nor credit units.
A present null limit or remaining value means unlimited allowance. Numeric fields can be omitted when a decision is denied or usage is unavailable; do not interpret an absent field as zero or unlimited. Use allowed and reason to handle the decision, and rediscover contract v4 before updating your client.
Deutsch
billing.entitlements.get verwendet jetzt Vertrag v4. Ersetzen Sie ai_requests und ai_credits durch ai_usage im usage-Selektor der Anfrage und lesen Sie die Antwort unter usage.ai_usage. Die bisherigen Metriknamen werden nicht mehr akzeptiert.
AI-Beträge sind ganzzahlige Mikro-Euro: 1 EUR = 1.000.000 Mikro-Euro. Wenn vorhanden, enthält usage.ai_usage.limit das verfügbare Gesamtkontingent des Nutzungszeitraums einschließlich gekaufter Aufladungen; usage.ai_usage.current enthält den bisherigen Verbrauch und usage.ai_usage.remaining das verbleibende Kontingent. Teilen Sie Zahlenwerte durch 1.000.000, um Euro anzuzeigen. Diese Werte sind weder Anfragezahlen noch Credit-Einheiten.
Ein vorhandener null-Wert für das Limit oder verbleibende Kontingent bedeutet unbegrenztes Kontingent. Zahlenfelder können bei einer Ablehnung oder nicht verfügbarem Verbrauch fehlen; behandeln Sie fehlende Felder weder als null Euro noch als unbegrenzt. Beachten Sie allowed und reason und rufen Sie vor der Client-Aktualisierung Vertrag v4 über die Discovery ab.
{ "usage": ["ai_usage"] }
1. Choose a credential: OAuth for hosted clients, a token for headless agents
For a direct API integration, sign in as an account owner or admin and open the API token settings page (Settings → Integrations → API tokens). Choose a read or write scope bundle, review its operation picker, and create the token. The UI resolves each selected bundle to the exact public canonical operation ids at issuance time; changing a bundle later never changes an existing token.
A newly created secret is shown once—store it in a secret manager. Never paste a secret into a URL, source repository, or log. The session-only api.tokens.* operations behind this page are never grantable to a credential.
Headless agents can manage their own credentials without the settings page. An owner or admin issues one bootstrap API token with the admin:tokens scope bundle. That credential may call api.tokens.agent.create to mint narrower tokens, api.tokens.agent.rotate to rotate one, api.tokens.agent.revoke to revoke one, and api.tokens.agent.list to find token ids. Scopes never widen: every exact operation id a minted or rotated token receives must already be held by the calling credential, so a token can only include admin:tokens when its issuer holds it, and a credential can never revoke or rotate itself. Every mint is audited and listed on the settings page with its origin credential, and admin:tokens is the only bundle that can grant these operations. OAuth grants never carry it, so an MCP or OAuth client cannot manage credentials.
curl --silent --show-error -X POST "$MOREFLOW_API_BASE/v1/operations/api.tokens.agent.create" \
-H "Authorization: Bearer $MOREFLOW_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"label":"Nightly sync agent","scopes":["clients:read"],"idempotencyKey":"nightly-sync-2026-09-03"}'
Every integration token is bound to the account that issued it. Do not include accountId, account_id, or x-moreflow-account-id to select another account: the API derives the account from the verified bearer token.
OAuth 2.1 is the default path for hosted MCP clients such as Claude.ai, ChatGPT, and Claude Desktop. Point the client at the Moreflow MCP endpoint. A 401 response points it at the protected-resource metadata, which names the API host as the authorization server. The API serves authorization-server metadata, /oauth2/authorize, /oauth2/token, /oauth2/register. Authorization codes require PKCE (S256). Moreflow uses revocable opaque access tokens, so authorization-server metadata intentionally does not advertise a jwks_uri. During consent you sign in, pick the Moreflow account the grant is bound to, and approve one scope bundle; the issued access token carries exactly that account and bundle, and every token can be revoked from the API token settings page. No manual bearer is needed for a client that completes OAuth. Moreflow does not advertise or accept the OAuth client_credentials grant: this server has no service-principal/account-binding model, so use a static account-bound token or a user-consented OAuth grant instead.
Client identity: clients may present an HTTPS client-id metadata URL (CIMD, MCP profile 2026-07-28) without prior registration, or use RFC 7591 dynamic registration. Unauthenticated dynamic registration is only open on the development API host (dev-api.moreflow.io) and on local loopback runtimes; on production, registration requires an authenticated Moreflow session, so a production client without CIMD support must be registered while signed in as an account admin or fall back to a pre-issued token. In every environment, verify that the requested resource is exactly the API host’s /mcp URL (for production https://api.moreflow.io/mcp) and never approve a different audience.
Static account-bound tokens remain the path for headless agents, servers, CLIs, and any client that cannot complete a browser consent. Both credential kinds resolve to the same account binding, the same exact-operation scopes, and the same rate limits.
2. Send your first public API call
Use the bearer token in the Authorization header. This read uses the public clients.list operation; its exact input and output evolve through the live contract linked above.
export MOREFLOW_API_BASE="https://api.moreflow.io"
export MOREFLOW_API_TOKEN="mfo_replace_with_your_secret"
curl --silent --show-error "$MOREFLOW_API_BASE/v1/operations/clients.list" \
-H "Authorization: Bearer $MOREFLOW_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"limit":25}'
Successful public API calls return the operation output directly as the JSON response body. There is no { success: true, data } success wrapper; for this list operation, the top-level body follows the discovered output schema:
{
"items": [],
"nextCursor": null
}
The token needs the exact clients.list scope. Add operations.discovery.list and operations.discovery.get when your integration or agent will discover contracts at runtime.
3. Discover before you invoke
Do not hardcode a catalog from this guide. List the public surface, inspect one operation, and construct requests from its returned schema and examples. A token can discover only the exact operation ids it is allowed to use.
# Find public operations that the token can discover.
curl --silent --show-error "$MOREFLOW_API_BASE/v1/operations/operations.discovery.list" \
-H "Authorization: Bearer $MOREFLOW_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{}'
# Inspect one live input/output/error contract and its safety metadata.
curl --silent --show-error "$MOREFLOW_API_BASE/v1/operations/operations.discovery.get" \
-H "Authorization: Bearer $MOREFLOW_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"id":"clients.list"}'
Use operations.discovery.search for a keyword search and operations.modules.list to narrow the browse path. The Swagger reference renders the same public surface, while the OpenAPI JSON is suitable for generated clients.
4. Follow the error envelope and recovery
Failures use the canonical { success: false, error: { code, message, details, recovery } } envelope. Treat error.recovery as the next navigational action instead of matching human-readable messages. Preserve its operation id and safe arguments when they are present.
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Integration token does not include the requested operation scope.",
"details": { "operationId": "clients.create" },
"recovery": {
"operation": "operations.discovery.list",
"args": {}
}
}
}
| Signal | Meaning | Recovery |
|---|---|---|
401 AUTH_ERROR | The bearer is missing, invalid, expired, or revoked. | Create or rotate an account-bound token and send it as Authorization: Bearer. |
403 FORBIDDEN | The token or session cannot use this operation or account. | Discover the allowed public operation ids; request the missing exact scope from an account administrator. |
404 NOT_FOUND | The operation is unknown or not externally discoverable. | Call operations.discovery.list, then describe a returned id instead of guessing. |
402 UPGRADE_REQUIRED | The account monthly api_requests allowance is exhausted. | Follow the pricing recovery in the error and check billing.entitlements.get. |
429 RATE_LIMIT | A short-window protective limit was reached. | Honor Retry-After and RateLimit-* headers, then retry safely. |
5. Cursor pagination
Public list contracts return { items, nextCursor }. Their input uses an optional cursor and a limit from 1 to 100, defaulting to 25. Send the opaque cursor returned by the previous page as the next request’s cursor; never decode, construct, or reuse a cursor with different filters.
- Start without a cursor and choose a limit within the discovered range.
- Read items from the response and continue only when
nextCursoris not null. - Send that exact opaque value with the same filters; stop when
nextCursoris null.
{"limit":25,"cursor":"opaque-next-cursor"}
6. Mutation idempotency
Every idempotent public mutation has one durable lifecycle: one key = one logical request. Generate a key for a new payload, reuse it only for a byte-identical retry, and generate a new key when the user edits the payload. Send the key either as the Idempotency-Key header or as the JSON idempotencyKey field when the operation schema exposes it; if both are supplied, keep them identical.
Created
The request claimed the key and executed. Store the successful result if the user needs it; clear the local pending attempt only after success.
Replayed
The identical request reused a stored terminal result. HTTP responses include Idempotency-Replayed: true; MCP responses expose replayed: true in structured content.
Conflict
409 IDEMPOTENCY_CONFLICT means the key already belongs to another fingerprint. Rotate the key for the edited/new payload; never clear a key and retry a changed request under that same value.
Idempotency recovery table
| Signal | Meaning | Next action |
|---|---|---|
IDEMPOTENCY_CONFLICT (409) | The key is bound to a different request. | Use a new key for the new payload. Reuse the old key only if restoring the exact original payload. |
IDEMPOTENCY_IN_PROGRESS (425) | An identical request is still running or its 600-second pending lease has not expired. | Honor Retry-After and retry the identical request with the same key. After the lease expires, use the documented safe re-execution option if available. |
Idempotency-Replayed: true | The stored terminal success or failure was returned. | Treat the response as the result of the original attempt. Do not submit again with another key. |
Successes and terminal failures are retained for 30 days. A failed request therefore replays the same failure during that window; rotate only when the request itself is being changed. After expiry the key may be reused. Pending claims have a 600-second lease; an active claim returns the exact remaining delay in Retry-After, while an expired claim reports unknown outcome unless safe re-execution is explicitly enabled.
Copy-paste browser recipe
This framework-agnostic helper keeps the key and its payload fingerprint together in sessionStorage. It reuses a key for a timeout retry, rotates it for an edited payload, and clears it after success. It calls the API directly from the page, with no token: the public form endpoints answer CORS requests from any site, and the form’s allowed domains decide which sites may submit.
async function sha256(value) {
const bytes = new TextEncoder().encode(value);
const digest = await crypto.subtle.digest('SHA-256', bytes);
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('');
}
async function submitPublicForm(formId, data) {
const storageKey = 'moreflow:idempotency:' + formId;
const payload = { formId, data };
const payloadHash = await sha256(JSON.stringify(payload));
const previous = JSON.parse(sessionStorage.getItem(storageKey) || 'null');
const key = previous && previous.payloadHash === payloadHash
? previous.key
: crypto.randomUUID();
sessionStorage.setItem(storageKey, JSON.stringify({ key, payloadHash }));
const response = await fetch('https://api.moreflow.io/v1/forms/' + encodeURIComponent(formId) + '/submissions', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Idempotency-Key': key },
body: JSON.stringify({ data, idempotencyKey: key })
});
if (response.ok) sessionStorage.removeItem(storageKey);
if (response.headers.get('Idempotency-Replayed') === 'true') {
console.info('Moreflow returned the stored result for this retry.');
}
if (!response.ok) throw new Error('Moreflow submission failed: ' + response.status);
return response.json();
}
For production clients, preserve the same request body and key across network retries, and inspect the structured error.code/error.recovery envelope instead of matching messages. For a new custom browser form, install @moreflow/[email protected] and call submitWithRetry({ formId, input: { data }, acquisitionTracking: () => consentGiven }); use normalizePhoneNumber() only for phone fields. The helper supplies replay-safe idempotency without imposing a UI framework. Browser forms call https://api.moreflow.io directly with no API token; configure their allowed domains.
7. Verify webhook signatures
Create a webhook endpoint with webhooks.endpoints.create and store the signing secret exactly once. Moreflow sends the raw JSON body with an x-moreflow-signature header containing t=<unix-seconds>, v1=<hex-hmac>. The signed payload is the exact timestamp, a period, and the exact raw body: timestamp + "." + rawBody. Every envelope also carries its causing invocation’s requestId, operationId, and actor: { type, id }. Verify before JSON parsing or reserialization, compare in constant time, reject timestamps outside the 300-second replay window (five minutes), and accept the previous secret only during the documented rotation overlap. Call webhooks.events.list with { level: 2 } for each event’s version, occurrence key, and payload schema. See the webhook correlation and verification follow-up (moreflow-hjbpv.21) for the evolving event envelope and versioned catalog contract.
whsec_test; timestamp 1700000000; raw body {"id":"evt_test","type":"client.created"}; expected v1 HMAC 98b6c5a0e2aeac155732a1bb05a3cdd112ea587c57a154cfe67e4bceae47fe5a.Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
const REPLAY_WINDOW_SECONDS = 300;
export function verifyMoreflowWebhook(
rawBody,
signatureHeader,
secret,
nowSeconds = Math.floor(Date.now() / 1000)
) {
const values = signatureHeader.split(',').map((value) => value.trim());
const timestamp = values.find((value) => /^t=\d+$/u.test(value))?.slice(2);
const signatures = values
.filter((value) => /^v1=[0-9a-f]{64}$/iu.test(value))
.map((value) => value.slice(3).toLowerCase());
if (!timestamp || signatures.length === 0) return false;
const timestampSeconds = Number(timestamp);
if (!Number.isSafeInteger(timestampSeconds) ||
Math.abs(Math.floor(nowSeconds) - timestampSeconds) > REPLAY_WINDOW_SECONDS) {
return false;
}
const signedPayload = Buffer.concat([Buffer.from(timestamp + '.', 'ascii'), rawBody]);
const expected = createHmac('sha256', secret).update(signedPayload).digest();
return signatures.some((signature) => {
const received = Buffer.from(signature, 'hex');
return received.length === expected.length && timingSafeEqual(expected, received);
});
}
// Read the request bytes once, verify them, then JSON.parse(rawBody.toString('utf8')).
Python
import hashlib
import hmac
import re
import time
REPLAY_WINDOW_SECONDS = 300
def verify_moreflow_webhook(
raw_body: bytes,
signature_header: str,
secret: str,
now_seconds: int | None = None,
) -> bool:
values = [value.strip() for value in signature_header.split(',')]
timestamp = next(
(value[2:] for value in values if re.fullmatch(r't=\d+', value)),
None,
)
signatures = [
value[3:].lower()
for value in values
if re.fullmatch(r'v1=[0-9a-f]{64}', value, re.IGNORECASE)
]
if timestamp is None or not signatures:
return False
timestamp_seconds = int(timestamp)
current_seconds = int(time.time()) if now_seconds is None else now_seconds
if abs(current_seconds - timestamp_seconds) > REPLAY_WINDOW_SECONDS:
return False
signed_payload = timestamp.encode('ascii') + b'.' + raw_body
expected = hmac.new(secret.encode('utf-8'), signed_payload, hashlib.sha256).digest()
return any(
hmac.compare_digest(expected, bytes.fromhex(signature))
for signature in signatures
)
# Flask: raw_body = request.get_data(cache=False, as_text=False)
# Read the bytes once, verify them, then json.loads(raw_body).
curl / OpenSSL test
secret='whsec_test'
timestamp='1700000000'
raw_body='{"id":"evt_test","type":"client.created"}'
signed_payload="$timestamp.$raw_body"
expected=$(printf '%s' "$signed_payload" | openssl dgst -sha256 -hmac "$secret" | awk '{print $2}')
test "$expected" = '98b6c5a0e2aeac155732a1bb05a3cdd112ea587c57a154cfe67e4bceae47fe5a'
# This shell equality is a test-vector assertion only; receivers must use
# timingSafeEqual (Node.js) or hmac.compare_digest (Python).
curl --request POST 'https://receiver.example/webhooks/moreflow' \
--header 'Content-Type: application/json' \
--header 'x-moreflow-signature: t=1700000000, v1=98b6c5a0e2aeac155732a1bb05a3cdd112ea587c57a154cfe67e4bceae47fe5a' \
--data-binary "$raw_body"
8. Limits, allowance, and retries
402 UPGRADE_REQUIRED
The account’s monthly api_requests allowance is exhausted. It is an account plan entitlement, consumed when an external operation is admitted and not refunded if that operation later fails. Follow the returned pricing recovery.
429 rate limited
A short-window protective rate limit was reached. It is independent from the monthly allowance: honor Retry-After, RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset, then retry safely.
Discovery calls are unmetered. A successful operations.discovery.list, operations.discovery.get, operations.discovery.search, or billing.entitlements.get call does not consume the monthly API-request allowance; invoking a business operation does.
9. MCP Streamable HTTP quickstart
Moreflow exposes a stateless Streamable HTTP MCP server at /mcp on the dedicated API host. Configure a remote MCP client with the same account-bound bearer token and API base URL:
{
"mcpServers": {
"moreflow": {
"url": "https://api.moreflow.io/mcp",
"headers": { "Authorization": "Bearer mfo_replace_with_your_secret" }
}
}
}
The server exposes exactly eleven meta-tools: list_operations, list_modules, describe_operation, query_operation, the write tools create_record, update_record, delete_record, and run_action, and get_entitlements, plus search and fetch. It does not generate one MCP tool per API operation.
https://app.moreflow.io/mcp is deliberately 404; only the API host's /mcp endpoint is live. The API host also serves host-derived protected-resource metadata at /.well-known/oauth-protected-resource and authorization-server metadata at /.well-known/oauth-authorization-server, so hosted clients complete OAuth 2.1 with PKCE and account selection; CLI, desktop, and headless clients can configure a static account-bound bearer directly instead.
Use list, search, fetch, and describe to obtain the current public contract. Call query_operation for a read-only operation and the write tool that describe_operation names in its tool field for a mutation, with its operationId, input, and a caller-generated idempotencyKey. Each write tool accepts only its own operations (create, update in place, delete or irreversible removal, or an action such as sending, publishing, or a job); a mismatch is rejected with structured recovery naming the right tool. When the discovered agent metadata says confirmation: "always", the invocation must include confirm: true after explicit user confirmation.
Listing, describing, searching, and entitlement reads are unmetered. The write tools delegate to the same API dispatcher as HTTP calls, so they apply token scopes, account binding, rate limits, audit records, canonical recovery failures, and the monthly api_requests meter.
Besides tools, the server advertises MCP resources and prompts. resources/list returns the quickstart (moreflow://docs/quickstart), the OpenAPI document (moreflow://openapi), and one contract document per module; resources/templates/list exposes moreflow://operations/{operationId}/contract and moreflow://operations/{operationId}/output-schema. Reading a resource is unmetered and needs no tool call. prompts/list offers the canonical workflows onboard_client, quote_to_invoice, and schedule_meeting. The tool list can change when the account policy changes: initialize advertises tools.listChanged, and a client that opens GET /mcp with Accept: text/event-stream receives notifications/tools/list_changed and should then call tools/list again.
Result envelope: a successful query_operation or write tool call returns the operation output itself as structuredContent (never wrapped in { success, data }), and a failure returns the canonical { success: false, error } envelope with isError: true. Every result carries _meta.operationId and _meta.outputSchema.uri, the resource whose JSON Schema the successful structuredContent conforms to, so a client can validate results without calling describe_operation first.
10. Per-client MCP setup
Use the client-specific installation option below. OAuth clients complete Moreflow OAuth 2.1 in the client; bearer clients use the secret-free placeholder in the generated snippet. Never commit a bearer token to a config file.
Claude.ai
Add the Moreflow MCP URL and complete the OAuth 2.1 consent flow; no bearer token is pasted. https://api.moreflow.io/mcp Or use the one-click install link: Claude.ai
Claude Code
Use this static bearer installation snippet and replace the token placeholder locally: claude mcp add --transport http moreflow https://api.moreflow.io/mcp --header "Authorization: Bearer {{MOREFLOW_API_TOKEN}}"
ChatGPT
Add the Moreflow MCP URL and complete the OAuth 2.1 consent flow; no bearer token is pasted. https://api.moreflow.io/mcp
Cursor
Use this static bearer installation snippet and replace the token placeholder locally: {
"mcpServers": {
"moreflow": {
"url": "https://api.moreflow.io/mcp",
"headers": {
"Authorization": "Bearer {{MOREFLOW_API_TOKEN}}"
}
}
}
} Or use the one-click install link: Cursor
VS Code
Use this static bearer installation snippet and replace the token placeholder locally: {
"servers": {
"moreflow": {
"type": "http",
"url": "https://api.moreflow.io/mcp",
"headers": {
"Authorization": "Bearer {{MOREFLOW_API_TOKEN}}"
}
}
}
} Or use the one-click install link: VS Code
After connection, initialize the session, call list_operations, use query_operation for reads, and call describe_operation for the exact operation you need before using the write tool it names for a mutation. Preserve the canonical error envelope, safety metadata, and idempotency rules inside MCP just as you would for HTTP.
SDKs and agent plugins
The browser SDKs call the same public operations as this guide and contain no UI, so you keep your own markup and styles. Each README has a complete example. They call https://api.moreflow.io directly from the visitor’s browser: the public form, chatbot, and booking endpoints answer CORS requests from any site, so you need no proxy and no API token. Never put an API token in browser code; @moreflow/api is a server-only client for token-authenticated calls.
Route the browser SDKs through your own server only when your Content Security Policy cannot list the API host. Preserving per-visitor rate limiting would require forwarding the real client IP through infrastructure Moreflow trusts. Cloudflare currently rejects caller-supplied CF-Connecting-IP and the public API does not trust X-Forwarded-For, so all visitors share one rate limit at your server’s IP. Forward the browser’s Origin header unchanged: dropping or spoofing it defeats Allowed Domains enforcement (forms reject a missing Origin; chatbots skip the allowlist without Origin or Referer). Never set a fixed Origin or add an API token in the proxy.
| Package | Purpose | Install | Docs |
|---|---|---|---|
| @moreflow/forms | Load, validate, and submit a custom Moreflow form, including Turnstile, the honeypot, and phone verification. | npm install @moreflow/[email protected] | README |
| @moreflow/meetings | Build a native booking widget: meeting type, slots, reservation, and booking. | npm install @moreflow/[email protected] | README |
| @moreflow/chatbots | Build a native chatbot client with visitor and thread continuity. | npm install @moreflow/[email protected] | README |
| @moreflow/tracking | Capture acquisition data (click ids, UTM, referrer, Meta and TikTok cookies); forms, meetings, and chatbots use it only with acquisitionTracking consent and otherwise send just the page URL's UTM parameters and click ids, without cookies or storage. | npm install @moreflow/[email protected] | README |
| @moreflow/api | SERVER-ONLY typed client for every public operation, generated from the contract snapshots. Never ship its token to browsers. | npm install @moreflow/[email protected] | README |
@moreflow/analytics | Website analytics and A/B testing: loads the moreflow tracker, queues events, and reads experiment variants, with optional React bindings. | Not published yet. | README |
@moreflow/site-kit | Sections, SEO helpers, and analytics bindings for TanStack Start websites on the moreflow site platform or your own Cloudflare account. | Not published yet. | README |
Use baseUrl for the API origin and fetchImpl for a custom transport across SDKs. Browser clients default to https://api.moreflow.io; use baseUrl: '' only for the optional CSP proxy described above. Their acquisition override is trackingCapture: an options object or a callback returning that SDK’s tracking metadata, called only while acquisitionTracking allows capture. Explicit payload tracking remains independent. Forms can bind these defaults once with createFormsClient({ baseUrl, acquisitionTracking, trackingCapture, fetchImpl }). Server code passes a static bearer or OAuth access token through createMoreflowApiClient({ token, fetchImpl, baseUrl }); never expose that token to a browser. A server client using an empty base URL needs a custom fetch implementation that resolves relative URLs to a trusted origin.
The agent plugins connect the hosted MCP server and add skills for reading records, onboarding a client, quote to invoice, and scheduling a meeting. Install the Claude Code plugin with claude plugin install moreflow --marketplace moreflow-io/agent-plugins, then run /mcp to sign in. For Codex run codex plugin marketplace add moreflow-io/agent-plugins and codex plugin add moreflow@moreflow. Source and issues: moreflow-io/agent-plugins.