Skip to content
PricingDocsKO

Service kit

Selected public API declarations from @shard-lab/agentpay-service-kit. Import from the root or from a subpath (/intent, …).

function · service.d.mts

Signed calls from a service to the gateway.

function createMerchantClient(gatewayUrl: string): MerchantClient;

function · service.d.mts

Starts a service MCP: /mcp, /intents/, /agentpay/mandates, /.well-known/agentpay-service, /health.

function startServiceMcp({ id, name, version, port, bind, publicUrl, gatewayUrl, instructions, tools, callTool, routes, onMandate, allowedOrigins, log }: ServiceMcpOptions): ServiceMcp;

type · service.d.mts

type Intent = import("./intent.mjs").Intent;

type · service.d.mts

type Call = import("./intent.mjs").Call;

type · service.d.mts

type ToolDef = import("./mcp.mjs").ToolDef;

type · service.d.mts

type CallTool = import("./mcp.mjs").CallTool;

type · service.d.mts

Delivered by the gateway server to server when the user approves a mandate that allows merchant charges. Store it like a card token: it can charge only inside that one mandate.

type MandateCredential = {
mandate_id: string;
credential_id: string;
secret: string;
};

type · service.d.mts

type GatewayReply = {
http: number;
error?: string;
message?: string;
[k: string]: any;
};

type · service.d.mts

type MerchantClient = {
/**
* Reads the live mandate (status, remaining, runs).
*/
mandate: (cred: MandateCredential) => Promise<GatewayReply>;
/**
* Runs one charge inside the mandate. The same `charge_id` runs at most once (a replay returns the first result).
*/
charge: (cred: MandateCredential, charge: {
charge_id: string;
summary: string;
spend_okrw: number;
calls: Call[];
}) => Promise<GatewayReply>;
/**
* Gives up the mandate (stops charges at once).
*/
release: (cred: MandateCredential, reason?: string) => Promise<GatewayReply>;
};

type · service.d.mts

type IntentInput = Omit<Intent, "type" | "version" | "id" | "service" | "expires_at" | "calls" | "spend_okrw"> & {
calls?: Call[];
spend_okrw?: number;
};

type · service.d.mts

type BuiltIntent = {
intent_id: string;
intent_url: string;
intent: Intent & {
created_at: number;
};
};

type · service.d.mts

type ServiceMcpOptions = {
/**
* Service id as registered with the tenant (`[a-z0-9-]`, 2..40).
*/
id: string;
/**
* Display name.
*/
name: string;
version?: string;
port: number;
/**
* Default 127.0.0.1. Use 0.0.0.0 in a container.
*/
bind?: string;
/**
* The registered origin (https in production). Intent URLs are built from it.
*/
publicUrl?: string;
/**
* Needed for merchant charges.
*/
gatewayUrl?: string;
/**
* MCP server instructions for the agent.
*/
instructions?: string;
tools: ToolDef[] | (() => ToolDef[]);
callTool: CallTool;
/**
* Extra HTTP routes. Return undefined to fall through to 404.
*/
routes?: (req: import("node:http").IncomingMessage, res: import("node:http").ServerResponse, url: URL, helpers: {
json: Function;
readJson: Function;
}) => Promise<unknown>;
/**
* Receives a confirmed mandate credential. Throw to refuse it.
*/
onMandate?: (delivery: {
credential: MandateCredential;
intentId: string;
mandate: GatewayReply;
}) => void | Promise<void>;
/**
* Browser origins allowed to call /mcp.
*/
allowedOrigins?: string[];
log?: (...a: unknown[]) => void;
};

type · service.d.mts

type ServiceMcp = {
/**
* Builds an intent and serves it at `<publicUrl>/intents/<id>` for 15 minutes.
*/
intent: (body: IntentInput) => BuiltIntent;
merchant: MerchantClient | undefined;
server: import("node:http").Server;
ready: Promise<void>;
base: string;
close: () => Promise<void>;
};

function · intent.d.mts

Compiles a mandate to the scope the gateway checks calls against.

function compileScope(mandate: Mandate, { asset, unit }: {
asset: AssetRef;
unit: bigint;
}): Scope;

function · intent.d.mts

function callAllowed(scope: Scope, call: Call): string | undefined;

function · intent.d.mts

function callsCovered(scope: Scope, calls: Call[]): {
ok: true;
} | {
ok: false;
reason: string;
index: number;
};

function · intent.d.mts

function validateIntent(raw: unknown, { service, asset, unit, now }: {
service: ServiceRef;
asset: AssetRef;
unit: bigint;
now: number;
}): Intent;

function · intent.d.mts

function validateCharge(b: unknown): ChargeRequest;

function · intent.d.mts

function word(v: bigint | number | string): Hex;

function · intent.d.mts

function selectorOf(signature: string): Hex;

function · intent.d.mts

function mandateDigest(mandate: Mandate): Hex;

function · intent.d.mts

function periodWindow(validAfter: number, seconds: number, at: number): {
index: number;
start: number;
end: number;
};

const · intent.d.mts

The type every intent carries.

const INTENT_TYPE: "agentpay.intent";

const · intent.d.mts

Largest intent JSON the gateway accepts.

const MAX_INTENT_BYTES: number;

const · intent.d.mts

const APPROVE_SELECTOR: "0x095ea7b3";

const · intent.d.mts

const FULL_MASK: string;

class · intent.d.mts

Thrown by the validators. code is a stable machine code (e.g. CONTRACT_NOT_REGISTERED), why the detail.

class IntentError extends Error {
/** @param {string} why @param {string} [code] */
constructor(why: string, code?: string);
code: string;
why: string;
}

type · intent.d.mts

type Hex = `0x${string}`;

type · intent.d.mts

One contract call. value must be “0” (payments use the ERC-20 payment asset).

type Call = {
target: string;
value?: string;
data: string;
};

type · intent.d.mts

A condition on one 32-byte argument (arg is the zero-based argument index). Give exactly one value kind: okrw (whole payment-asset units, scaled by the asset decimals), uint, bytes32 or address.

type Predicate = {
arg: number;
op: "eq" | "lte";
okrw?: number;
uint?: string;
bytes32?: string;
address?: string;
};

type · intent.d.mts

One function the mandate allows on one registered contract, e.g. deposit(uint256).

type Permission = {
target: string;
name?: string;
signature: string;
predicates: Predicate[];
};

type · intent.d.mts

Fixed windows from valid_after (V4 period limits). At least one of max_runs or max_spend_okrw.

type Period = {
seconds: number;
max_runs?: number;
max_spend_okrw?: number;
};

type · intent.d.mts

type MandateUpdate = {
add_total_okrw?: number;
add_calls?: number;
add_days?: number;
per_okrw?: number;
};

type · intent.d.mts

type IntentAction = "purchase" | "mandate" | "mandate.update";

type · intent.d.mts

type ServiceRef = {
id: string;
contracts: string[];
merchantCharges?: boolean;
};

type · intent.d.mts

type AssetRef = {
address: string;
};

type · intent.d.mts

A merchant-initiated charge (the user is not present).

type ChargeRequest = {
mandate_id: string;
charge_id: string;
summary: string;
spend_okrw: number;
calls: Call[];
};

type · intent.d.mts

The scope and limits the user approves once. The wallet contract enforces the same limits on chain.

type Mandate = {
/**
* Stable id within the service. Must start with `<service id>:` (e.g. `acme:plan`).
*/
key: string;
/**
* Short label shown on the approval screen.
*/
label: string;
/**
* Total the mandate may spend.
*/
total_okrw: number;
/**
* Most one run may spend.
*/
per_okrw: number;
/**
* Most one payment-token `approve` inside a run may allow.
*/
approve_cap_okrw: number;
/**
* Number of runs (default 100, 1..1000).
*/
calls?: number;
/**
* Validity in days (default 30, 1..365).
*/
days?: number;
/**
* Unix seconds the mandate starts (reservations).
*/
valid_after?: number;
/**
* Whether the service may charge later without the agent (needs a registry opt-in).
*/
merchant_charges?: boolean;
/**
* Per-window limits.
*/
period?: Period;
/**
* Display names of the places the service may pick.
*/
venues?: string[];
/**
* 1..16 functions on contracts registered to the service.
*/
permissions: Permission[];
};

type · intent.d.mts

What a service asks the wallet to run. Served as JSON at an absolute URL on the service’s registered origin.

type Intent = {
type: "agentpay.intent";
version: 1;
/**
* `int_` + 8..64 url-safe characters.
*/
id: string;
/**
* The service id. Must match the origin the intent was fetched from.
*/
service: string;
action: IntentAction;
/**
* One line for the user, up to 160 characters.
*/
summary: string;
/**
* What the calls spend now.
*/
spend_okrw: number;
/**
* Balance the wallet should hold for this mandate (top-up hint).
*/
prefund_okrw?: number;
/**
* Unix seconds, at most one hour ahead.
*/
expires_at: number;
/**
* Calls to run now (0..8). They must already fit the mandate.
*/
calls: Call[];
mandate: Mandate | {
key: string;
};
/**
* For `mandate.update` only.
*/
update?: MandateUpdate;
};

type · intent.d.mts

A mandate compiled to the wallet policy’s own terms (target, selector, argument predicates).

type Scope = {
asset: string;
approveCapWei: string;
spenders: string[];
permissions: Array<{
target: string;
name?: string;
signature: string;
selector: Hex;
predicates: Array<{
offset: number;
operator: string;
mask: string;
expected: string;
}>;
}>;
};

function · auth.d.mts

function newCredentialId(): string;

function · auth.d.mts

Gateway side: the credential secret, derived (never stored).

function deriveSecret(masterKey: string | Buffer, credentialId: string): string;

function · auth.d.mts

Service side: the Authorization header for one request to the gateway.

function signRequest({ credentialId, secret, method, path, body, ts }: {
credentialId: string;
secret: string;
method: string;
path: string;
body?: string;
ts?: number;
}): string;

function · auth.d.mts

Gateway side: checks a signed request.

function verifyRequest(header: string | undefined, { masterKey, method, path, body, now, maxSkew }: {
masterKey: string | Buffer;
method: string;
path: string;
body?: string;
now?: number;
maxSkew?: number;
}): {
ok: true;
credentialId: string;
} | {
ok: false;
code: "CREDENTIAL_REQUIRED" | "REQUEST_EXPIRED" | "BAD_SIGNATURE";
};

const · auth.d.mts

The Authorization scheme of a signed service request.

const SCHEME: "AgentPay-Mandate-HMAC";

const · auth.d.mts

const MAX_SKEW_SECONDS: 300;

function · mcp.d.mts

function checkArgs(schema: InputSchema | undefined, args: unknown): string | undefined;

function · mcp.d.mts

Streamable HTTP MCP handler. Mount handle on your own HTTP server at /mcp.

function createMcpHttp({ serverInfo, instructions, tools, callTool, authenticate, onUnauthorized, allowedOrigins, maxSessions, sessionTtlMs, keepAliveMs, log }: {
serverInfo: ServerInfo;
instructions?: string;
tools: ToolDef[] | (() => ToolDef[]);
callTool: CallTool;
authenticate?: (req: import("node:http").IncomingMessage) => unknown | Promise<unknown>;
onUnauthorized?: (res: import("node:http").ServerResponse) => void;
allowedOrigins?: string[];
maxSessions?: number;
sessionTtlMs?: number;
keepAliveMs?: number;
log?: (...a: unknown[]) => void;
}): {
handle(req: import("node:http").IncomingMessage, res: import("node:http").ServerResponse): Promise<void>;
sessions: Map<string, unknown>;
};

function · mcp.d.mts

The same tools over stdio (one client). Reads JSON-RPC lines from stdin, writes to stdout.

function serveStdio({ serverInfo, instructions, tools, callTool, onInitialize, log }: {
serverInfo: ServerInfo;
instructions?: string;
tools: ToolDef[] | (() => ToolDef[]);
callTool: CallTool;
onInitialize?: (info: {
caps: Record<string, unknown>;
clientName?: string;
clientInfo?: unknown;
}) => void | Promise<void>;
log?: (...a: unknown[]) => void;
}): {
ui: Ui;
clientName(): string | undefined;
caps(): Record<string, unknown>;
};

function · mcp.d.mts

Minimal Streamable HTTP client for tests and service-to-service checks.

function mcpHttpClient(url: string, { token, onElicit, clientInfo, elicitation }?: {
token?: string;
onElicit?: (message: string) => Promise<"accept" | "decline" | "cancel">;
clientInfo?: {
name: string;
version: string;
};
elicitation?: boolean;
}): Promise<McpClient>;

const · mcp.d.mts

MCP protocol versions this server speaks, newest first.

const PROTOCOL_VERSIONS: string[];

type · mcp.d.mts

type McpClient = {
/**
* The initialize result.
*/
init: Record<string, any>;
sessionId: () => string | null;
rpc: (method: string, params?: unknown) => Promise<{
response: any;
headers: Headers;
events: any[];
}>;
tools: () => Promise<ToolDef[]>;
/**
* Parsed JSON result of one tool.
*/
call: (name: string, args?: Record<string, unknown>) => Promise<any>;
close: () => Promise<void>;
};

type · mcp.d.mts

The JSON Schema subset tool arguments are checked against (type, required, enum, range, maxLength).

type InputSchema = {
type: "object";
properties?: Record<string, {
type?: string;
description?: string;
enum?: unknown[];
minimum?: number;
maximum?: number;
maxLength?: number;
}>;
required?: string[];
additionalProperties?: boolean;
};

type · mcp.d.mts

type ToolDef = {
name: string;
title?: string;
description: string;
inputSchema: InputSchema;
annotations?: Record<string, unknown>;
};

type · mcp.d.mts

type ServerInfo = {
name: string;
title?: string;
version: string;
};

type · mcp.d.mts

type Prompt = {
answer: Promise<string>;
cancel(): void;
};

type · mcp.d.mts

Prompts the user through the agent app (MCP elicitation). canPrompt is false when the client cannot show one.

type Ui = {
canPrompt: boolean;
showLink(message: string): Prompt;
ask?(message: string, timeoutMs?: number): Promise<string>;
};

type · mcp.d.mts

type ToolContext = {
principal: unknown;
session: {
caps?: Record<string, unknown>;
clientName?: string;
};
ui: Ui;
signal?: AbortSignal;
};

type · mcp.d.mts

Runs one tool. Return a JSON-able object; { error: "CODE", message } marks the result as an error.

type CallTool = (name: string, args: Record<string, any>, ctx: ToolContext) => unknown | Promise<unknown>;

function · registry.d.mts

function originOf(url: string): string;

function · registry.d.mts

function loadRegistry(path: string, env?: Record<string, string | undefined>): Registry;

function · registry.d.mts

The service whose registered origin serves url.

function serviceByUrl(reg: Registry, url: string): RegistryService | undefined;

function · registry.d.mts

function contractByAddress(reg: Registry, address: string): any;

function · registry.d.mts

function serviceById(reg: Registry, id: string): RegistryService | undefined;

type · registry.d.mts

type RegistryService = {
id: string;
name: string;
/**
* scheme + host + port the service serves intents from.
*/
origin: string;
mcp: string;
merchantCharges: boolean;
/**
* Lowercase addresses.
*/
contracts: string[];
};

type · registry.d.mts

type Registry = {
asset: {
address: string;
symbol: string;
decimals: number;
type?: string;
};
shops?: any[];
finance?: any;
services: RegistryService[];
[k: string]: any;
};

function · testing.d.mts

function startTestGateway({ service, asset, port }: {
service: {
id: string;
origin: string;
contracts: string[];
merchantCharges?: boolean;
};
asset?: {
address: string;
decimals?: number;
};
port?: number;
}): Promise<TestGateway>;

type · testing.d.mts

type Intent = import("./intent.mjs").Intent;

type · testing.d.mts

type Mandate = import("./intent.mjs").Mandate;

type · testing.d.mts

type TestMandate = {
mandate_id: string;
service: string;
intent_id: string;
key: string;
label: string;
status: "active" | "released" | "revoked" | "expired";
valid_after: number;
valid_until: number;
per_okrw: number;
total_okrw: number;
remaining_okrw: number;
calls_left: number;
period?: import("./intent.mjs").Period;
runs: Array<{
charge_id?: string;
summary: string;
spend_okrw: number;
by: "agent" | "service";
run_at: number;
}>;
};

type · testing.d.mts

type TestGateway = {
/**
* Pass this as the service's `gatewayUrl`.
*/
url: string;
/**
* Fetches and validates an intent from the service's origin, approves its mandate, runs its calls, and (when the
* mandate allows merchant charges) delivers a credential to `<origin>/agentpay/mandates`.
*/
approve: (intentUrl: string) => Promise<{
mandate_id: string;
intent: Intent;
ran: boolean;
credential_delivered: boolean;
}>;
mandate: (mandateId: string) => TestMandate | undefined;
/**
* The user turns the mandate off.
*/
revoke: (mandateId: string) => void;
/**
* Moves the gateway clock forward for mandates (period windows, expiry).
*/
advance: (seconds: number) => void;
close: () => Promise<void>;
};