Weft API Reference
Build buyer applications that discover and pay for resources on Weft.
Quickstart
Connecting an AI agent? Use https://weft.network/setup.md instead — it connects the account with browser OAuth, and there is no API key to paste into the conversation. This quickstart is for developers building their own buyer application.
Weft's buyer API uses a wk_* key created in the dashboard. The signup and
sign-in API endpoints do not issue a bearer token.
- Create a Weft account and confirm your email.
- Create a buyer API key.
- Copy the
wk_*key when it is shown. It cannot be displayed again. - Store it as
WEFT_API_KEYand make your first request.
export WEFT_API_KEY='wk_your_key_here'
curl --fail-with-body https://weft.network/api/v1/me \
--header "Authorization: Bearer $WEFT_API_KEY"
Search is authenticated and free for buyers in the current release:
curl --fail-with-body https://weft.network/api/v1/search \
--header "Authorization: Bearer $WEFT_API_KEY" \
--header "Content-Type: application/json" \
--data '{"query":"company enrichment API","max_results":3}'
Guides
Human-oriented guides live on weftlabs.com:
Agent bootstrap
An agent without a Weft account can start a 30-minute, email-claimed
bootstrap. It asks the human only for an email address—never a password—and
returns one wbt_* secret. Creation is public and rate-limited per IP and
normalized email.
curl --fail-with-body https://weft.network/api/v1/account_bootstraps \
--header "Content-Type: application/json" \
--data '{
"email":"human@example.com",
"agent_name":"Research agent",
"host_name":"Claude Code",
"reason":"Find weather data"
}'
The response never says whether that email already has an account. A separate,
private claim link goes only to the email address. The human creates an account
or freshly signs in, reviews the agent context and fixed access, enters the
matching user_code shown by the agent, then approves or rejects it. The
server capability set is not caller-configurable.
Successful create, status, and cancel responses use the normal top-level
data envelope. If the mail relay cannot accept the claim email, creation
returns a retryable 503 and leaves no bootstrap row or unusable credential.
Treat wbt_* exactly like an API key: do not print, paste, log, or commit it.
Before claim it can call only search, status, and cancel while the 30-minute
claim window remains open. After approval, the same bearer becomes durable and
can call identity, search, balance, fetch, purchase history, status, and
self-revocation. It cannot call seller, organization, API-key administration,
dashboard-session, transfer, withdrawal, or MCP surfaces. Human revocation from
Connections denies every subsequent request immediately.
Beyond a possible one-time signup grant (campaign-gated), there is no promotional balance or treasury subsidy in this flow. Paid fetch starts only after human claim, normal wallet provisioning, and funding — the human adds money at https://weft.network/dashboard/wallet.
Base URLs
- Production:
https://weft.network
Authentication
Send credentials in the bearer header:
Authorization: Bearer <credential>
- Credential identity: both
wk_*andax_live_*keys authorizeme; the response identifies whether the credential represents a buyer User or a seller Organization. - Buyer runtime: use a dashboard-created
wk_*key forbalance,search,fetch, and purchase history. - Seller administration: organization and resource-management operations
require an
ax_live_*resource key. These keys do not authorize buyer-only runtime operations. - Connected clients: an OAuth access token can authorize the buyer operations covered by its scopes.
- Claimed agents: a promoted
wbt_*bearer can authorize only its fixed buyer-runtime allowlist until the human revokes it. - Public operations: signup, sign-in, account recovery, OpenAPI download, and resource enrollment do not require a bearer credential.
Treat every key as a secret. Keep it out of source control and client-side browser bundles, and rotate it from the dashboard if it is exposed.
TypeScript SDK
TypeScript is Weft's supported application SDK. WeftClient is the primary
buyer interface; generated API classes remain available for advanced use.
npm install @weftlabs/sdk
import { WeftClient } from "@weftlabs/sdk";
const apiKey = process.env.WEFT_API_KEY;
if (!apiKey) {
throw new Error("Set WEFT_API_KEY to a buyer wk_* API key");
}
const weft = new WeftClient({
apiKey,
...(process.env.WEFT_BASE_URL ? { baseUrl: process.env.WEFT_BASE_URL } : {}),
});
const account = await weft.me();
const search = await weft.search({ query: "weather data API" });
console.log(JSON.stringify({ account: account.data, results: search.results }));
Python, Ruby, and Go packages are generated API clients. They are available for
developers who need low-level access, but they are not yet at feature parity
with the supported TypeScript WeftClient experience.
SDK examples are shown alongside endpoints where a verified example is available.
Charge for your API
Everything above is the buyer side: you spend. This section is the seller side: you get paid. An agent pays per request, and the money settles to your wallet. Weft never holds it.
1. Create a seller key
Selling needs a seller key, not the buyer wk_* key above.
- Create a seller API key.
- Copy the
ax_live_*key when it is shown. It cannot be displayed again. - Store it as
WEFT_SELLER_API_KEY.
A buyer wk_* key spends. A seller ax_live_* key gets paid. They are not
interchangeable, and the middleware needs the seller key.
2. Install
npm install @weftlabs/sdk @x402/core @x402/evm express
The SDK carries the payment middleware. @x402/evm carries the scheme that
prices the route and shapes the payment. Both are required: a route whose
scheme is not registered is rejected at startup.
3. Set the environment
| Variable | Meaning |
|---|---|
WEFT_SELLER_API_KEY |
Your ax_live_* seller key. Authenticates settlement. |
WEFT_PAY_TO |
The wallet address that receives the money. |
WEFT_NETWORK |
CAIP-2 chain id. Defaults to eip155:8453 (Base). |
X402_FACILITATOR_URL |
Optional override. Defaults to https://x402.weft.network. |
Treat WEFT_SELLER_API_KEY like any other secret: never in source control,
never in a browser bundle.
4. Protect a route — Express
import express from "express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { weftPaymentMiddleware } from "@weftlabs/sdk/facilitator/middleware";
const apiKey = process.env.WEFT_SELLER_API_KEY;
if (!apiKey) {
throw new Error("Set WEFT_SELLER_API_KEY to a seller ax_live_* API key");
}
const payTo = process.env.WEFT_PAY_TO;
if (!payTo) {
throw new Error("Set WEFT_PAY_TO to the wallet address that gets paid");
}
const network = process.env.WEFT_NETWORK ?? "eip155:8453";
const app = express();
app.use(
weftPaymentMiddleware(
{
"GET /v1/quote": {
accepts: { scheme: "exact", network, payTo, price: "$0.01" },
},
},
{
apiKey,
name: "Acme Pricing API",
type: "api",
tags: ["finance", "pricing"],
schemes: [{ network, server: new ExactEvmScheme() }],
},
),
);
app.get("/v1/quote", (_req, res) => {
res.json({ symbol: "ACME", price: "12.34", currency: "USD" });
});
const port = Number(process.env.PORT ?? 3000);
app.listen(port, () => {
console.log(JSON.stringify({ listening: port }));
});
5. Or with Hono
weftPaymentMiddlewareHono takes the same configuration:
import { Hono } from "hono";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { weftPaymentMiddlewareHono } from "@weftlabs/sdk/facilitator/middleware";
const apiKey = process.env.WEFT_SELLER_API_KEY;
if (!apiKey) {
throw new Error("Set WEFT_SELLER_API_KEY to a seller ax_live_* API key");
}
const payTo = process.env.WEFT_PAY_TO;
if (!payTo) {
throw new Error("Set WEFT_PAY_TO to the wallet address that gets paid");
}
const network = process.env.WEFT_NETWORK ?? "eip155:8453";
const app = new Hono();
app.use(
weftPaymentMiddlewareHono(
{
"GET /v1/quote": {
accepts: { scheme: "exact", network, payTo, price: "$0.01" },
},
},
{
apiKey,
name: "Acme Pricing API",
type: "api",
tags: ["finance", "pricing"],
schemes: [{ network, server: new ExactEvmScheme() }],
},
),
);
app.get("/v1/quote", (c) =>
c.json({ symbol: "ACME", price: "12.34", currency: "USD" }),
);
export default app;
What happens per request
An unpaid caller does not reach your handler. It receives 402 Payment
Required with a PAYMENT-REQUIRED header describing the price, the network
and the payee. The caller pays, retries with a PAYMENT-SIGNATURE header, and
this time your handler runs and returns its normal response.
Behind that, the middleware calls the facilitator twice. /verify checks the
buyer's payment before your handler runs. /settle moves the money after your
handler returns success.
/settle rejects a call without your seller key — every settlement returns
401 and you are never paid. /verify does not require the key, but it reads
it when present, and the facilitator uses it to attribute verification
attempts to your product. Set apiKey once and both are covered.
Your handler is never charged for twice: a response with status 400 or above is returned to the caller without settling.
When something fails
| What you see | What it means | What to do |
|---|---|---|
[weft] facilitator sync failed at boot |
The facilitator was unreachable when the process started. | Nothing. The server keeps serving and re-syncs in the background. |
402 with Settlement failed |
The settlement call did not complete. | Retryable by the caller. |
| Every settlement fails with 401 | The seller key is missing, malformed, or from another organization. | Seller action: check WEFT_SELLER_API_KEY. |
| Startup rejects the route | The route's scheme and network pair has no registered implementation. | Seller action: pass schemes as the examples do. |
402 challenges name no payee |
payTo is unset. |
Seller action: set WEFT_PAY_TO. |
Advanced: the facilitator REST contract
The middleware is the normal path. If you implement x402 yourself, the
facilitator exposes /supported, /verify, and /settle directly at
https://x402.weft.network. That contract is documented in the
x402-facilitator repository.
You do not need it to charge for a route.
The Weft API powers the weft CLI, the hosted MCP server
(weft.network/mcp), and third-party applications that discover and pay
for resources on Weft. The buyer runtime covers six concerns:
- Account creation and recovery (
/api/v1/auth/*) - Credential identity (
/api/v1/me) - Wallet visibility (
/api/v1/balance) - Discovery (
/api/v1/search) - Paid execution (
/api/v1/fetch) - Purchase history (
/api/v1/purchases)
Buyer runtime calls require a dashboard-created wk_* account key, an
OAuth access token with the relevant scope, or a claimed wbt_* bearer on
its fixed allowlist. The organization-scoped API key and payment operations
in this document are seller administration surfaces and require an
ax_live_* resource key. These credential types are not interchangeable.
This document is a deliberate subset of the /api/v1 surface, not an
inventory of it. The seller-side management endpoints — /api/v1/agents,
/api/v1/webhook_endpoints, and /api/v1/analytics — are reachable with
an ax_live_* key but are intentionally left undocumented: they are
dashboard-facing and not part of the published client contract. Nothing
outside this document is a supported contract, and the response-validation
gate in config/environments/test.rb enforces only what is declared here.
Before claim, a wbt_* bearer is temporary and permits only search plus its
own status/cancel operations for 30 minutes. Human approval binds it to the
User and promotes the same secret to durable identity, search, balance,
fetch, purchase-history, status, and revocation capabilities. It remains
valid until revoked. Seller, organization, API-key administration,
dashboard-session, transfer, withdrawal, and MCP surfaces always refuse it.
Bootstrap lifecycle successes follow the API-standard { "data": ... }
envelope. All errors share the envelope defined by ErrorResponse, except the
buyer-runtime endpoints (/search, /fetch) which use bespoke
envelopes carrying additional context — see SearchErrorResponse and
FetchErrorResponse.
Authentication
HTTP Authentication, scheme: bearer Dashboard
wk_*key, OAuth token, or a claimedwbt_*bearer on its fixed identity, search, balance, fetch, and purchase-history allowlist.HTTP Authentication, scheme: bearer A
wbt_*credential. Before claim it permits search, status, and cancellation for 30 minutes. Human approval promotes the same bearer to a fixed durable capability set until revocation.
Agent Bootstrap
Temporary search-only access followed by human promotion of the same bearer.
Create a temporary agent bootstrap
POST /api/v1/account_bootstraps
Creates a 30-minute, search-only bootstrap and emails a separate claim
link to the supplied address. Human approval binds the same wbt_*
bearer to the account and promotes it to the fixed durable capability
set. The response is deliberately identical for new and existing
account emails and never contains the claim token.
Rate limits apply per IP and normalized email; request bodies over 4 KiB
are rejected before parsing. The bootstrap row and one-time credentials
are committed only after the mail relay accepts the
claim email. A retryable 503 leaves no bootstrap row behind.
Request body
{
"email": "human@example.com",
"agent_name": "Research agent",
"host_name": "OpenCode",
"reason": "Find weather data"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | AccountBootstrapRequest | true | none |
201 Response
{
"data": {
"id": "boot_01K3C9Y7Q4R6T8V2W5X0Z1A3B4",
"status": "pending",
"capabilities": [
"search",
"status",
"cancel"
],
"expires_at": "2026-08-22T14:30:00Z",
"approval": {
"method": "email_link",
"expires_in": 1800,
"interval": 5,
"user_code": "W3FT-7K2M"
},
"temporary_api_key": "wbt_example_store_as_a_secret"
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 201 | Created | Bootstrap created after the claim email is accepted for delivery | AccountBootstrapCreatedResponse |
| 413 | Payload Too Large | Request body exceeds the 4 KiB limit | ErrorResponse |
| 422 | Unprocessable Entity | Invalid bootstrap context | ErrorResponse |
| 429 | Too Many Requests | Rate limit exceeded | RateLimitResponse |
| 503 | Service Unavailable | Claim email delivery is temporarily unavailable; no bootstrap was created and the request may be retried | ErrorResponse |
Response Headers
| Status | Header | Type | Format | Description |
|---|---|---|---|---|
| 503 | Retry-After | string | Suggested delay before retrying, in seconds. |
No authentication required
Read bootstrap lifecycle status
GET /api/v1/account_bootstraps/{id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | true | none |
200 Response
{
"data": {
"id": "boot_01K3C9Y7Q4R6T8V2W5X0Z1A3B4",
"status": "claimed",
"capabilities": [
"identity",
"search",
"balance",
"fetch",
"purchases",
"status",
"revoke"
],
"expires_at": null,
"approval": {
"method": "email_link",
"expires_in": 0,
"interval": 5
}
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Current lifecycle and polling guidance while the credential can authenticate | AccountBootstrapStatusResponse |
| 401 | Unauthorized | Unknown ID or non-matching bootstrap credential | ErrorResponse |
Cancel or revoke a bootstrap credential
DELETE /api/v1/account_bootstraps/{id}
A pending bootstrap becomes rejected. A claimed promoted credential becomes revoked and fails every subsequent request immediately.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | true | none |
200 Response
{
"data": {
"id": "boot_01K3C9Y7Q4R6T8V2W5X0Z1A3B4",
"status": "claimed",
"capabilities": [
"identity",
"search",
"balance",
"fetch",
"purchases",
"status",
"revoke"
],
"expires_at": null,
"approval": {
"method": "email_link",
"expires_in": 0,
"interval": 5
}
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Rejected or revoked terminal lifecycle status | AccountBootstrapStatusResponse |
| 401 | Unauthorized | Unknown ID or non-matching bootstrap credential | ErrorResponse |
Auth
Account creation and authentication flows.
Create an account
POST /api/v1/auth/sign_up
Request body
{
"email": "user@example.com",
"password": "string",
"password_confirmation": "string"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | SignUpRequest | true | none |
200 Response
{
"data": {
"user": {
"id": 0,
"email": "user@example.com",
"status": "string"
},
"token": "string",
"confirmation_required": true
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Account created | AuthResponse |
| 422 | Unprocessable Entity | Validation error | ErrorResponse |
| 429 | Too Many Requests | Rate limit exceeded (20 accounts/hour/IP) | ErrorResponse |
No authentication required
Confirm an account
POST /api/v1/auth/confirm
Request body
{
"confirmation_token": "string"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | ConfirmRequest | true | none |
200 Response
{
"data": {
"user": {
"id": 0,
"email": "user@example.com",
"status": "string"
},
"token": "string",
"confirmation_required": true
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Account confirmed | AuthResponse |
| 422 | Unprocessable Entity | Invalid token | ErrorResponse |
No authentication required
Resend confirmation email
POST /api/v1/auth/resend-confirmation
Request body
{
"email": "user@example.com"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | ResendConfirmationRequest | true | none |
200 Response
{
"data": {
"message": "string"
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Confirmation sent | MessageResponse |
No authentication required
Sign in with email and password
POST /api/v1/auth/sign_in
Request body
{
"email": "user@example.com",
"password": "string"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | SignInRequest | true | none |
200 Response
{
"data": {
"user": {
"id": 0,
"email": "user@example.com",
"status": "string"
},
"token": "string",
"confirmation_required": true
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Signed in | AuthResponse |
| 401 | Unauthorized | Invalid credentials | ErrorResponse |
No authentication required
Request password reset
POST /api/v1/auth/password/reset
Request body
{
"email": "user@example.com"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | PasswordResetRequest | true | none |
200 Response
{
"data": {
"message": "string"
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Reset email sent | MessageResponse |
No authentication required
Update password with reset token
POST /api/v1/auth/password/update
Request body
{
"reset_password_token": "string",
"password": "string",
"password_confirmation": "string"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | PasswordUpdateRequest | true | none |
200 Response
{
"data": {
"message": "string"
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Password updated | MessageResponse |
| 422 | Unprocessable Entity | Invalid or expired reset token | ErrorResponse |
No authentication required
Account
Current user account information.
Get the current credential principal
GET /api/v1/me
Returns the seller Organization represented by an ax_live_* resource
key, or the buyer User represented by a wk_* account key, OAuth access
token, or claimed wbt_* bearer. Branch on data.principal_type.
200 Response
{
"data": {
"principal_type": "organization",
"id": 0,
"name": "string",
"slug": "string",
"kind": "string",
"api_key": {
"id": 0,
"name": "string",
"created_at": "2019-08-24T14:15:22Z",
"last_used_at": "2019-08-24T14:15:22Z",
"created_by": {
"id": 0,
"email": "user@example.com",
"display_name": "string"
}
}
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Current Organization or User principal | MeResponse |
| 401 | Unauthorized | Unauthorized | ErrorResponse |
Requires authentication: Bearer token
Verified SDK example
WeftClient.me()
import { WeftClient } from "@weftlabs/sdk";
const apiKey = process.env.WEFT_API_KEY;
if (!apiKey) {
throw new Error("Set WEFT_API_KEY to a buyer wk_* API key");
}
const weft = new WeftClient({
apiKey,
...(process.env.WEFT_BASE_URL ? { baseUrl: process.env.WEFT_BASE_URL } : {}),
});
const account = await weft.me();
const search = await weft.search({ query: "weather data API" });
console.log(JSON.stringify({ account: account.data, results: search.results }));
API Keys
Manage seller organization/resource keys (ax_live_*). Buyer runtime
keys (wk_*) are created and rotated in the buyer dashboard.
List API keys
GET /api/v1/api_keys
List ax_live_* resource keys for the seller Organization.
200 Response
{
"data": [
{
"id": 0,
"name": "string",
"key_prefix": "string",
"last_used_at": "2019-08-24T14:15:22Z",
"revoked_at": "2019-08-24T14:15:22Z",
"created_at": "2019-08-24T14:15:22Z"
}
],
"pagination": {
"page": 0,
"per_page": 0,
"total": 0,
"total_pages": 0
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | List of API keys | ApiKeyListResponse |
| 401 | Unauthorized | Unauthorized | ErrorResponse |
| 403 | Forbidden | The credential authenticated but has no organization context |
(ORGANIZATION_REQUIRED). Account-scoped (buyer) keys cannot read
this org-scoped endpoint.|ErrorResponse|
Requires authentication: Bearer token
Create an API key
POST /api/v1/api_keys
Create an ax_live_* resource key for the authenticated seller
Organization. Buyer wk_* keys are created in the buyer dashboard.
Request body
{
"name": "string"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | CreateApiKeyRequest | true | none |
201 Response
{
"data": {
"id": 0,
"name": "string",
"key_prefix": "string",
"raw_key": "string",
"created_at": "2019-08-24T14:15:22Z"
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 201 | Created | API key created (includes raw key, shown only once) | ApiKeyCreatedResponse |
| 401 | Unauthorized | Unauthorized | ErrorResponse |
| 422 | Unprocessable Entity | Validation error | ErrorResponse |
Requires authentication: Bearer token
Revoke an API key
DELETE /api/v1/api_keys/{id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | true | API key ID |
200 Response
{
"data": {
"message": "string"
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | API key revoked | MessageResponse |
| 401 | Unauthorized | Unauthorized | ErrorResponse |
| 404 | Not Found | API key not found | ErrorResponse |
Requires authentication: Bearer token
Balance
Read-only snapshot of the buyer's wallet, spending policy, and current-window spend (today / this week).
Get wallet, spending policy, and current-window spend
GET /api/v1/balance
Read-only snapshot for the buyer behind the bearer token. The
response always includes a promo block — values are zero in v1
and fill in once the freemium promo ledger ships, without a
shape change. The Base USDC balance is fetched
server-side through Crossmint. If Crossmint is unreachable, the
balance fields are null; consumers must not interpret that as zero.
Account-scoped: the bearer must be a buyer-scoped API key, an OAuth
access token carrying balance, or a claimed wbt_* bearer.
200 Response
{
"promo": {
"balance_usd": "0.00",
"scope": "weft-search",
"expires_at": "2019-08-24T14:15:22Z"
},
"wallet": {
"provider": "crossmint",
"address": "string",
"balance_usdc": "12.34",
"balance_tempo_usd": "2.03",
"total_usd": "12.34",
"network": "string"
},
"spent_today_usd": "0.0005",
"spent_week_usd": "3.10",
"policy_used_today_usd": "0.30",
"policy_used_week_usd": "3.30",
"policy": {
"max_tx_usd": "1.00",
"daily_limit_usd": "10.00",
"weekly_limit_usd": "50.00"
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Wallet + policy + spend snapshot | BalanceResponse |
| 401 | Unauthorized | Unauthorized — missing or non-buyer-scoped API key | ErrorResponse |
| 403 | Forbidden | The OAuth access token authenticated but lacks the balance |
scope (RFC 6750 insufficient_scope). Carries a
WWW-Authenticate: Bearer error="insufficient_scope", scope="balance"
header. wk_ API keys are unscoped and never see this.|InsufficientScopeResponse|
Requires authentication: Bearer token
Search
Semantic search across the Weft index of paid agent resources. Returns A2A-shaped AgentCards with pricing, skills, ranking, and endpoints.
Search the Weft index
POST /api/v1/search
Semantic search over the Weft index of paid agent resources. The
request body is the weft-search-platform /v1/search contract:
a free-text query, optional max_results, and optional structured
filters (price / priceatomic / type / protocol — the canonical
FilterSpec v1 vocabulary, vendored verbatim from the platform). Price is
a dual representation of one constraint: price in USD decimal strings
(the reasoning form) XOR `priceatomic` in integer micro-USD (the
settlement form) — mutually exclusive, set at most one.
Account-scoped: the bearer token must be a buyer-scoped API key, an
OAuth token carrying search, or a pending or claimed wbt_* bootstrap
bearer. Pending bootstrap access uses an anonymous bootstrap identity;
after claim, the promoted bearer uses its bound User. Rejection,
cancellation, expiry, and revocation end bootstrap search. Bootstrap
search is limited to 60 requests per credential and 120 per IP each
hour before native credential promotion.
Response negotiation: Accept: application/json (default) returns
the structured envelope; Accept: text/markdown returns a rendered
Markdown digest of the same results — useful for piping into a chat
UI or LLM prompt.
Compact view: send view: "compact" to get the agent view of the
hosted MCP weft_search tool. Each result is a small contract for the
next paid call (below 2,000 JSON bytes): typed inputs, the request
recipe, one access method with price, attribution, a ready next_call
to POST /api/v1/fetch when the request is determined, and the
authenticated contract_url for the full contract. Use it when you
give search results to a model. The compact rows are in
compact_results; results is empty. The compact view is JSON only
and needs the curated search backend.
Request body
{
"query": "send email from an agent",
"max_results": 10,
"filters": {
"price": {
"lte": "string",
"gte": "string",
"eq": "string",
"range_gte": "string",
"range_lte": "string"
},
"price_atomic": {
"lte": 0,
"gte": 0,
"eq": 0,
"range_gte": 0,
"range_lte": 0
},
"type": {
"eq": "api",
"in": [
"api"
]
},
"protocol": {
"eq": "x402",
"in": [
"x402"
]
},
"category": {
"eq": "string",
"in": [
"string"
]
},
"method": {
"eq": "GET",
"in": [
"GET"
]
},
"execution_mode": {
"eq": "sync",
"in": [
"sync"
]
},
"weft_fetch_compatible": true,
"include_unknown_prices": false
},
"view": "full"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | SearchRequest | true | none |
200 Response
{
"query_trace_id": "2e536172-f35a-49b0-b5b1-ca3970d0f446",
"query": "string",
"format": "json",
"markdown": "string",
"result_count": 0,
"served_from": "live",
"as_of": "2019-08-24T14:15:22Z",
"stale": true,
"payment_note": "string",
"applied_filters": {
"price": {
"lte": "string",
"gte": "string",
"eq": "string",
"range_gte": "string",
"range_lte": "string"
},
"price_atomic": {
"lte": 0,
"gte": 0,
"eq": 0,
"range_gte": 0,
"range_lte": 0
},
"type": {
"eq": "api",
"in": [
"api"
]
},
"protocol": {
"eq": "x402",
"in": [
"x402"
]
},
"category": {
"eq": "string",
"in": [
"string"
]
},
"method": {
"eq": "GET",
"in": [
"GET"
]
},
"execution_mode": {
"eq": "sync",
"in": [
"sync"
]
},
"weft_fetch_compatible": true,
"include_unknown_prices": false
},
"decomposition_source": "CALLER",
"embedder_model": "string",
"candidates_considered": 0,
"warnings": [
{
"code": "string",
"message": "string",
"cause": "string",
"context": {}
}
],
"match_quality": "strong",
"reason": "below_relevance_floor",
"suggestion": "string",
"results": [
{
"provider": {
"provider_id": "string",
"display_name": "string",
"origin_domains": [
"string"
]
},
"capability": {
"capability_id": "string",
"capability_type": "string"
},
"endpoints": [
{
"endpoint_id": "string",
"url": "string",
"resource_type": "string",
"primary_protocol": "string",
"call": {
"method": "string",
"input_schema": {},
"example_request": {},
"example_response": null
},
"price": {
"indexed_usd": "string",
"atomic": 0,
"source": "probe",
"last_observed_at": "2019-08-24T14:15:22Z",
"live_verified": true
},
"payment": [
{
"protocol": "string",
"scheme": "string",
"network": "string",
"asset": "string",
"amount": "string",
"pay_to": "string",
"max_timeout_seconds": 0,
"facilitator": "string"
}
],
"access_methods": [
{
"access_method_id": "string",
"protocol": "x402",
"scheme": "string",
"network": "string",
"asset": "string",
"asset_decimals": 0,
"price": {},
"merchant": {},
"terms": {},
"status": "listed",
"weft_fetch": {
"state": "supported",
"reason": "string",
"contract_version": 0,
"coverage": "terminal_response"
}
}
],
"service": {
"id": "string",
"name": "string"
},
"operation": {
"id": "string",
"name": "string",
"description": "string"
},
"docs": {
"website": "http://example.com",
"homepage": "http://example.com",
"api": "http://example.com",
"llms_txt": "http://example.com"
},
"contract_url": "http://example.com",
"output_schema": {},
"output": {},
"execution": {
"mode": "sync"
},
"callability": {
"state": "complete",
"missing": [
"string"
]
},
"compatibility": {},
"source": {
"catalog": "curated_marketplace",
"snapshot_version": 0,
"captured_at": "2019-08-24T14:15:22Z"
},
"operator_type": "first_party",
"operated_by_id": "string",
"settled_via_facilitator_id": "string",
"settlements": 0,
"last_verified_at": "2019-08-24T14:15:22Z",
"latency_p50_ms": 0
}
],
"score": 0
}
],
"view": "full",
"compact_results": [
{
"provider": {
"id": "string",
"name": "string"
},
"operation": {
"id": "string",
"name": "string",
"description": "string"
},
"inputs": {},
"request": {},
"example": null,
"access": {
"id": "string",
"protocol": "string",
"weft_fetch": {},
"price": {
"kind": "string",
"indexed_usd": "string",
"unit": "string",
"hint": "string"
}
},
"attribution": {
"search_id": "d5826b15-4ef0-45f4-80df-3b8d5f941dd6",
"operation_id": "string",
"access_method_id": "string"
},
"output": {},
"docs": {
"website": "http://example.com",
"homepage": "http://example.com",
"api": "http://example.com",
"llms_txt": "http://example.com"
},
"contract_url": "http://example.com",
"contract_detail": "string",
"completion": "string",
"blocked_scopes": [
{
"scope": "string",
"state": "string"
}
],
"contract_gaps": [
"string"
],
"readiness": {
"property1": {
"state": "string",
"scope": "string"
},
"property2": {
"state": "string",
"scope": "string"
}
},
"next_call": {
"method": "POST",
"path": "/api/v1/fetch",
"body": {},
"fill": [
"string"
]
}
}
],
"omitted_results": 1
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Search results | string |
| 401 | Unauthorized | Unauthorized — missing or non-buyer-scoped API key | ErrorResponse |
| 403 | Forbidden | The OAuth access token authenticated but lacks the search |
scope (RFC 6750 insufficient_scope). Carries a
WWW-Authenticate: Bearer error="insufficient_scope", scope="search"
header. wk_ API keys are unscoped and never see this.|InsufficientScopeResponse|
|406|Not Acceptable|The requested response format is not JSON or Markdown.|None|
|422|Unprocessable Entity|Invalid request — empty/missing query, out-of-range
max_results, an unknown top-level parameter, invalid
filters (unknown filter key/operator, bad enum value, or a
sub-filter without exactly one operator), or an invalid view
(unknown value, Accept: text/markdown, or a backend without the
compact view). See the error code.|SearchErrorResponse|
|429|Too Many Requests|Bootstrap search rate limit exceeded|RateLimitResponse|
|500|Internal Server Error|Internal server error|SearchErrorResponse|
|502|Bad Gateway|Search service unavailable|SearchErrorResponse|
Requires authentication: Bearer token
Verified SDK example
WeftClient.search(request)
import { WeftClient } from "@weftlabs/sdk";
const apiKey = process.env.WEFT_API_KEY;
if (!apiKey) {
throw new Error("Set WEFT_API_KEY to a buyer wk_* API key");
}
const weft = new WeftClient({
apiKey,
...(process.env.WEFT_BASE_URL ? { baseUrl: process.env.WEFT_BASE_URL } : {}),
});
const account = await weft.me();
const search = await weft.search({ query: "weather data API" });
console.log(JSON.stringify({ account: account.data, results: search.results }));
Fetch
The universal x402/MPP fetch proxy. POST any URL the agent wants to
reach, with a max_cost_usd ceiling; Weft handles the 402 Payment
Required challenge, authorizes payment from the buyer's wallet on
the selected rail, replays the request, and streams the upstream
artifact back base64-encoded. Errors always carry the buyer's current
policy + balance + dashboard URL so the caller can surface "what
changed?" without a second round-trip.
Pay-and-fetch any URL (x402/MPP proxy)
POST /api/v1/fetch
Universal x402/MPP fetch proxy. The caller provides a target url,
a merchant-principal max_cost_usd ceiling, and optional method / body /
headers. Weft:
- Issues the request.
- On
402 Payment Required, selects a supported x402 or MPP challenge. - Compares the asking price to
max_cost_usdand the buyer's policy (max_tx_usd, daily/weekly limits). - If refill is allowed and an eligible mainnet MPP challenge finds its Tempo token short,
creates or adopts a buyer-owned Base-to-Tempo refill and returns
409 FUNDING_PENDINGwithdetails.reason=funding_active. If an overlapping payment invalidates the balance observation before a bridge is created, the same retryable response carriesdetails.reason=balance_changed. The caller retries the identical fetch afterdetails.retry_after_seconds; that later request obtains a fresh challenge and reruns rail selection and all payment controls. - Authorizes payment from the buyer's wallet on the selected rail.
- Replays the request with the protocol-specific payment credential.
- Streams the upstream artifact back, base64-encoded under
body_base64, withpaid_usd,held_usd,payment_status,tx_hash,protocol, andpurchase_id.paid_usdis "0.00" until the charge is CONFIRMED settled — a signed-but- unsettled hold (the common case for x402, which settles asynchronously) reports its amount inheld_usdinstead, never inpaid_usd.purchase_idis the id thatGET /api/v1/purchases/{id}accepts, so a caller can read how a pending charge ends.
Opt-in safety controls: allow_tempo_refill: false prevents this
request from creating, adopting, or enqueueing a Base-to-Tempo bridge.
A Tempo shortfall returns 402 INSUFFICIENT_BALANCE; unrelated bridges
and their jobs remain unchanged. access_method_id is advisory and
does not enforce a payment rail.
max_total_cost_usd is an all-in ceiling covering merchant principal,
network gas, provider fees, and prerequisite buyer-paid operations.
It disables refill; explicitly combining it with allow_tempo_refill: true
returns 422 INCOMPATIBLE_FETCH_CONTROLS. Current wallet integrations
provide no supported binding all-in fee guarantee. Therefore all-in
requests return 402 TOTAL_COST_UNVERIFIABLE before payment preparation,
approval, reservation, or refill. This also refuses recovery of older
payments and SIWX wallet signing; it admits no positive paid route.
Increasing the ceiling does not resolve missing fee authority.
Omitted controls preserve existing behavior. Ordinary non-402 merchant
responses retain MERCHANT_RETURNED_NON_402. Stored-result replay is
historical payment information, not a new bounded authorization.
Errors are structured with a stable error code, and each error
response carries the buyer's policy, balance, and a
dashboard_url so a CLI can render an actionable message without
a second round-trip.
Account-scoped: the bearer must be a buyer-scoped API key, an OAuth
access token carrying fetch, or a claimed wbt_* bearer.
Forwarded headers: the caller's headers are passed through
to the upstream, except a denylist of hop-by-hop and
Weft-internal headers (host, authorization, cookie,
proxy-authorization, x-forwarded-*, x-real-ip,
x-payment, connection, upgrade). Up to 32 headers, 4 KB of
combined value bytes.
Request body
{
"url": "https://x402.api.agentmail.to/v0/inboxes",
"max_cost_usd": "0.05",
"allow_tempo_refill": false,
"max_total_cost_usd": "0.05",
"method": "GET",
"body": "string",
"headers": {
"Accept": "application/json",
"User-Agent": "my-agent/1.0"
},
"search_id": "11111111-1111-4111-8111-111111111111",
"operation_id": "string",
"access_method_id": "string"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| Idempotency-Key | header | string | false | Opaque caller-generated retry key. Reusing the same key for the |
| body | body | FetchRequest | true | none |
Detailed descriptions
Idempotency-Key: Opaque caller-generated retry key. Reusing the same key for the
same buyer converges on one paid fetch; keys are hashed and
namespaced by buyer before storage. Send this header for every
unattended or retryable paid request.
Effective safety controls participate in retry identity. Changing
them under a reserved key returns IDEMPOTENCY_CONFLICT; an older
unconstrained payment cannot become a bounded success on retry.
200 Response
{
"status": 200,
"headers": {
"property1": "string",
"property2": "string"
},
"body_base64": "string",
"paid_usd": "0.002",
"held_usd": "0.002",
"payment_status": "pending",
"tx_hash": "string",
"protocol": "x402",
"artifact_id": 0,
"purchase_id": "pur_4V3n9mQ0b7XyK2sLpR8tWc1d"
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Paid fetch succeeded; artifact streamed back base64-encoded. | FetchResponse |
| 401 | Unauthorized | Unauthorized — missing or non-buyer-scoped API key | ErrorResponse |
| 402 | Payment Required | Payment refused. EXCEEDED_MAX_COST (over the caller cap) or |
INSUFFICIENT_BALANCE (the buyer is genuinely short —
details.asset, when present, names the exact token the failed
check read). TOTAL_COST_UNVERIFIABLE means no binding upper bound
on all buyer costs is available; details.reason is
binding_cost_unavailable. No payment operation or refill is
authorized by this refusal. An earlier uncertain payment remains
uncertain; the refusal does not cancel or release it. These errors
use FetchErrorResponse.|FetchErrorResponse|
|403|Forbidden|Policy violation, missing Crossmint payment permission
(PAYMENT_AUTHORIZATION_REQUIRED — details carries the
wallet-page URL where the authenticated user can enable
payments), denylisted recipient, or a merchant challenge on a
chain outside the wallet's own environment — a testnet wallet may
not pay a mainnet challenge, nor the reverse
(WALLET_ENVIRONMENT_MISMATCH; terminal, not retryable). All four
use FetchErrorResponse. Alternatively an OAuth access token
lacking the fetch scope (InsufficientScopeResponse, RFC 6750
insufficient_scope, with a WWW-Authenticate challenge). The two
envelopes are disjoint; branch on the error value.|Inline|
|409|Conflict|IDEMPOTENCY_CONFLICT — this buyer already used the supplied
Idempotency-Key for a different fetch request. Generate a new key
for the new operation; retry the original operation unchanged.
Or WALLET_SETUP_INCOMPLETE — the buyer's wallet has not been
created yet, so there is nothing to pay from. details.wallet_url
carries the page where the authenticated user finishes setup. No
retry succeeds until they do.
Or ACCOUNT_CLOSING — account closure has started, so the wallet
cannot reserve a new payment.
Or FUNDING_PENDING — retry after details.retry_after_seconds.
details.reason is funding_active when a buyer-owned Base-to-Tempo
refill exists, or balance_changed when an overlapping payment
invalidated the balance observation before funding could start.
Or DELIVERY_REPLAY_UNAVAILABLE — the merchant already returned a
successful response for this settled MPP payment, but Weft could
not persist its bytes for replay. The merchant is not called again;
details.tx_hash identifies the settled payment. All five use
FetchErrorResponse.|FetchErrorResponse|
|413|Payload Too Large|Upstream artifact exceeded the proxy's size cap.|FetchErrorResponse|
|422|Unprocessable Entity|Invalid request fields, method, cost, body, headers, idempotency
key, or URL. Safety controls use INVALID_ALLOW_TEMPO_REFILL,
INVALID_MAX_TOTAL_COST_USD, or INCOMPATIBLE_FETCH_CONTROLS.
UNSUPPORTED_ASSET means the merchant requested an
asset this wallet cannot settle. UNSUPPORTED_PAYMENT_METHOD
means the MPP challenge requested a method Weft cannot execute.|FetchErrorResponse|
|424|Failed Dependency|MERCHANT_RETURNED_NON_402 — the upstream merchant is at fault: it
did not return a 402, or its 402 challenge was invalid. A 4xx (not
5xx) because Weft behaved correctly and the caller should act on
details.reason (e.g. pick another merchant); it also keeps the
error envelope intact through CDNs that replace 5xx bodies.
PAID_DELIVERY_FAILED — the MPP payment settled, but the merchant
did not deliver a successful response. details.tx_hash identifies
the settled payment; details.merchant_status is present when the
merchant returned an HTTP response.|FetchErrorResponse|
|502|Bad Gateway|Merchant connection failure (MERCHANT_CONNECTION_FAILED) or
settlement signing failed on Weft's side (SETTLEMENT_FAILED).
For merchant failures, details.phase identifies challenge or paid;
paid requests may have moved funds. Reuse the same idempotency key.
details.reason carries a stable payment sentence.
details.protocol names a direct payment rail when that is safe and
useful. It is absent for automatic-funding failures so provider,
bridge, and retry details stay internal.|FetchErrorResponse|
|504|Gateway Time-out|MERCHANT_TIMEOUT — the upstream merchant did not complete the
challenge or paid request inside Weft's transport budget.
details.phase names the request. When it is paid,
details.payment_may_have_moved is true because the credential was
already sent. Retry the identical operation with the same
Idempotency-Key.
SETTLEMENT_PENDING — the payment was submitted and the wallet
provider had not reached a terminal state before Weft's polling
budget ran out. NOTHING is known to have failed: the charge may
already have settled, so this is not SETTLEMENT_FAILED and the
amount is not lost. details.retry_with_same_idempotency_key is
true.
Retry the identical request shortly. Sending the same
Idempotency-Key resumes the same provider operation; a caller that
sends no key is also safe, because Weft resumes its own in-flight
operation for the same merchant, amount and network rather than
opening a second one. Either way the retry resolves the original
payment instead of charging twice.|FetchErrorResponse|
Response Schema
Enumerated Values
| Property | Value |
|---|---|
| error | INVALID_REQUEST |
| error | UNKNOWN_PARAMETER |
| error | INVALID_URL |
| error | INVALIDMAXCOST_USD |
| error | INVALIDALLOWTEMPO_REFILL |
| error | INVALIDMAXTOTALCOSTUSD |
| error | INCOMPATIBLEFETCHCONTROLS |
| error | UNSUPPORTED_METHOD |
| error | INVALID_BODY |
| error | INVALID_HEADERS |
| error | INVALIDIDEMPOTENCYKEY |
| error | IDEMPOTENCY_CONFLICT |
| error | ACCOUNT_CLOSING |
| error | EXCEEDEDMAXCOST |
| error | TOTALCOSTUNVERIFIABLE |
| error | FUNDING_PENDING |
| error | INSUFFICIENT_BALANCE |
| error | PAYMENTAUTHORIZATIONREQUIRED |
| error | WALLETSETUPINCOMPLETE |
| error | MERCHANTRETURNEDNON_402 |
| error | MERCHANT_TIMEOUT |
| error | MERCHANTCONNECTIONFAILED |
| error | PAIDDELIVERYFAILED |
| error | DELIVERYREPLAYUNAVAILABLE |
| error | ARTIFACTTOOLARGE |
| error | DENYLISTED_RECIPIENT |
| error | WALLETENVIRONMENTMISMATCH |
| error | UNSUPPORTED_ASSET |
| error | UNSUPPORTEDPAYMENTMETHOD |
| error | SETTLEMENT_FAILED |
| error | SETTLEMENT_PENDING |
| error | POLICYVIOLATIONMAX_TX |
| error | POLICYVIOLATIONDAILY |
| error | POLICYVIOLATIONWEEKLY |
| error | insufficient_scope |
Requires authentication: Bearer token
Payments
Seller Organization payment receipts.
List payments
GET /api/v1/payments
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| page | query | integer | false | Page number |
| per_page | query | integer | false | Items per page |
200 Response
{
"data": [
{
"id": 0,
"tx_hash": "string",
"payer_address": "string",
"recipient_address": "string",
"amount": 0,
"amount_formatted": "string",
"currency": "string",
"network": "string",
"resource_url": "string",
"resource_host": "string",
"settlement_latency_ms": 0,
"settled_at": "2019-08-24T14:15:22Z",
"api_key_name": "string"
}
],
"pagination": {
"page": 0,
"per_page": 0,
"total": 0,
"total_pages": 0
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | List of payments | PaymentListResponse |
| 401 | Unauthorized | Unauthorized | ErrorResponse |
| 403 | Forbidden | The credential authenticated but has no organization context |
(ORGANIZATION_REQUIRED). Account-scoped (buyer) keys cannot read
this org-scoped endpoint.|ErrorResponse|
Requires authentication: Bearer token
Get payment details
GET /api/v1/payments/{id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | true | Payment ID |
200 Response
{
"data": {
"id": 0,
"tx_hash": "string",
"payer_address": "string",
"recipient_address": "string",
"amount": 0,
"amount_formatted": "string",
"currency": "string",
"network": "string",
"resource_url": "string",
"resource_host": "string",
"settlement_latency_ms": 0,
"settled_at": "2019-08-24T14:15:22Z",
"api_key_name": "string"
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Payment details | PaymentResponse |
| 401 | Unauthorized | Unauthorized | ErrorResponse |
| 404 | Not Found | Payment not found | ErrorResponse |
Requires authentication: Bearer token
Purchases
Buyer signing and settlement history.
List buyer purchases
GET /api/v1/purchases
Returns the authenticated buyer's signing and settlement ledger. Unlike
/api/v1/payments, this endpoint is User-scoped and backed by
signed_events, so rejected and pending attempts are included.
Scoped to the buyer's own merchant spend, matching the dashboard's
Purchases page. Internal signings are excluded: historical refill
self-transfers (rebalance) and operator signings (manual) are not
payments the buyer made and never appear here.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| page | query | integer | false | Page number |
| per_page | query | integer | false | Items per page |
200 Response
{
"data": [
{
"id": 0,
"purchase_id": "pur_4V3n9mQ0b7XyK2sLpR8tWc1d",
"status": "signed",
"amount_usd": "string",
"recipient_address": "string",
"network": "string",
"protocol": "mpp",
"context": "string",
"search_id": "string",
"attribution_status": "attributed",
"tx_hash": "string",
"reject_reason": "string",
"failure_reason": "string",
"idempotency_key": "string",
"signed_at": "2019-08-24T14:15:22Z",
"settled_at": "2019-08-24T14:15:22Z",
"artifact": {
"id": 0,
"merchant_url": "http://example.com",
"bytes": 0,
"mime": "string"
}
}
],
"pagination": {
"page": 0,
"per_page": 0,
"total": 0,
"total_pages": 0
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Buyer purchase ledger | PurchaseListResponse |
| 401 | Unauthorized | Unauthorized — missing or non-buyer-scoped credential | ErrorResponse |
| 403 | Forbidden | OAuth token lacks the balance scope. |
InsufficientScopeResponse |
Requires authentication: Bearer token
Get a buyer purchase
GET /api/v1/purchases/{id}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | any | true | The opaque purchase_id (pur_…) that fetch returns, or the |
Detailed descriptions
id: The opaque purchase_id (pur_…) that fetch returns, or the
legacy integer id. Both resolve only within the caller's own
purchases. New callers should store and send purchase_id.
200 Response
{
"data": {
"id": 0,
"purchase_id": "pur_4V3n9mQ0b7XyK2sLpR8tWc1d",
"status": "signed",
"amount_usd": "string",
"recipient_address": "string",
"network": "string",
"protocol": "mpp",
"context": "string",
"search_id": "string",
"attribution_status": "attributed",
"tx_hash": "string",
"reject_reason": "string",
"failure_reason": "string",
"idempotency_key": "string",
"signed_at": "2019-08-24T14:15:22Z",
"settled_at": "2019-08-24T14:15:22Z",
"artifact": {
"id": 0,
"merchant_url": "http://example.com",
"bytes": 0,
"mime": "string"
}
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Buyer purchase details | PurchaseResponse |
| 401 | Unauthorized | Unauthorized — missing or non-buyer-scoped credential | ErrorResponse |
| 403 | Forbidden | OAuth token lacks the balance scope. |
InsufficientScopeResponse |
| 404 | Not Found | Purchase not found or owned by another buyer | ErrorResponse |
Requires authentication: Bearer token
Resources
Public, unauthenticated ghost-resource enrollment. Lets an agent self-register so its human controller can later claim it from the dashboard.
Self-enroll a ghost resource (public, no auth)
POST /api/v1/resources/enroll
Lets an unauthenticated agent (or its operator) self-enroll a
Resource as a "ghost" (no owning provider). The server generates the
slug and a single-use claim_token; the response also carries a
claim_url the agent hands to its human controller to claim the
resource from the dashboard.
Client-supplied slug and category are silently ignored: slugs are
server-generated from name (so callers cannot squat on reserved
names), and category is a post-claim concern set by a real human.
Rate-limited to 10 enrollments/hour/IP.
Request body
{
"kind": "agent",
"name": "Self-Enrolled Bot",
"description": "string",
"wallet_address": "string"
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | ResourceEnrollmentRequest | true | none |
201 Response
{
"data": {
"id": 0,
"name": "string",
"slug": "string",
"description": "string",
"kind": "agent",
"visibility": "public_profile",
"wallet_address": "string",
"category": "string",
"verified": true,
"claimed": true,
"created_at": "2019-08-24T14:15:22Z",
"stats": {
"total_paid": 0,
"total_received": 0,
"payments_made": 0,
"payments_received": 0,
"unique_counterparties": 0,
"first_seen": "2019-08-24T14:15:22Z",
"last_active": "2019-08-24T14:15:22Z"
},
"claim_token": "string",
"claim_url": "string"
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 201 | Created | Ghost resource created | ResourceEnrollmentResponse |
| 422 | Unprocessable Entity | Validation error (missing/blank fields, or unknown kind) |
ErrorResponse |
| 429 | Too Many Requests | Rate limit exceeded (10 enrollments/hour/IP) | ErrorResponse |
No authentication required
Documentation
This document, served for tooling that reads the spec directly.
Fetch this OpenAPI document
GET /docs/openapi.yaml
200 Response
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | OpenAPI 3.1 document (YAML) | string |
No authentication required
Schemas
AccountBootstrapRequest
{
"email": "human@example.com",
"agent_name": "Research agent",
"host_name": "OpenCode",
"reason": "Find weather data"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| string(email) | true | none | none | |
| agent_name | string | true | none | none |
| host_name | string | false | none | none |
| reason | string | false | none | none |
| oauthclientid | string | false | none | Transitional compatibility for older CLI releases that use OAuth device exchange. |
| requested_scopes | [string] | false | none | Transitional compatibility for older CLI releases that use OAuth device exchange. |
AccountBootstrapCreated
{
"id": "boot_01K3C9Y7Q4R6T8V2W5X0Z1A3B4",
"status": "pending",
"capabilities": [
"search",
"status",
"cancel"
],
"expires_at": "2026-08-22T14:30:00Z",
"approval": {
"method": "email_link",
"expires_in": 1800,
"interval": 5,
"user_code": "W3FT-7K2M"
},
"temporary_api_key": "wbt_example_store_as_a_secret"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | string | true | none | none |
| status | string | true | none | none |
| capabilities | [string] | true | none | none |
| expires_at | string,null(date-time)¦null | true | none | Claim-window expiry. Null after the credential is promoted. |
| approval | AccountBootstrapCreatedApproval | true | none | none |
| temporaryapikey | string | true | none | Returned once. Store as a secret; it cannot be recovered. |
| device_code | string | false | none | Transitional response field for older CLI releases that use OAuth device exchange. |
Enumerated Values
| Property | Value |
|---|---|
| status | pending |
| status | claimed |
| status | rejected |
| status | expired |
| status | consumed |
| status | revoked |
AccountBootstrapCreatedResponse
{
"data": {
"id": "boot_01K3C9Y7Q4R6T8V2W5X0Z1A3B4",
"status": "pending",
"capabilities": [
"search",
"status",
"cancel"
],
"expires_at": "2026-08-22T14:30:00Z",
"approval": {
"method": "email_link",
"expires_in": 1800,
"interval": 5,
"user_code": "W3FT-7K2M"
},
"temporary_api_key": "wbt_example_store_as_a_secret"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | AccountBootstrapCreated | true | none | none |
AccountBootstrapStatusResponse
{
"data": {
"id": "boot_01K3C9Y7Q4R6T8V2W5X0Z1A3B4",
"status": "claimed",
"capabilities": [
"identity",
"search",
"balance",
"fetch",
"purchases",
"status",
"revoke"
],
"expires_at": null,
"approval": {
"method": "email_link",
"expires_in": 0,
"interval": 5
}
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | AccountBootstrapStatus | true | none | none |
AccountBootstrapStatus
{
"id": "boot_01K3C9Y7Q4R6T8V2W5X0Z1A3B4",
"status": "claimed",
"capabilities": [
"identity",
"search",
"balance",
"fetch",
"purchases",
"status",
"revoke"
],
"expires_at": null,
"approval": {
"method": "email_link",
"expires_in": 0,
"interval": 5
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | string | true | none | none |
| status | string | true | none | none |
| capabilities | [string] | true | none | none |
| expires_at | string,null(date-time)¦null | true | none | Claim-window expiry. Null after the credential is promoted. |
| approval | AccountBootstrapApproval | true | none | none |
Enumerated Values
| Property | Value |
|---|---|
| status | pending |
| status | claimed |
| status | rejected |
| status | expired |
| status | consumed |
| status | revoked |
AccountBootstrapApproval
{
"method": "email_link",
"expires_in": 0,
"interval": 1
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| method | string | true | none | none |
| expires_in | integer | true | none | none |
| interval | integer | true | none | Minimum status polling interval in seconds. |
Enumerated Values
| Property | Value |
|---|---|
| method | email_link |
AccountBootstrapCreatedApproval
{
"method": "email_link",
"expires_in": 0,
"interval": 1,
"user_code": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| method | string | true | none | none |
| expires_in | integer | true | none | none |
| interval | integer | true | none | Minimum status polling interval in seconds. |
| user_code | string | true | none | Show this code to the human; approval requires a matching normalized code. |
Enumerated Values
| Property | Value |
|---|---|
| method | email_link |
RateLimitResponse
{
"error": "too_many_requests",
"error_description": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| error | string | true | none | none |
| error_description | string | true | none | none |
Enumerated Values
| Property | Value |
|---|---|
| error | toomanyrequests |
ErrorResponse
{
"error": {
"code": "unauthorized",
"message": "Invalid or expired API key",
"request_id": "req_01HX9F3K7B2N4P8Q1R6T8Y2Z5"
}
}
Standard error envelope returned by all /api/v1 endpoints except
/search and /fetch (which use bespoke envelopes carrying
additional context). error.code is stable across releases and
safe to branch on; error.message is human-readable and may change.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| error | Error | true | none | A single error inside an ErrorResponse envelope. code is thestable machine-readable identifier (snakecase), message ishuman-readable, details carries optional structured context(e.g. validation field breakdown), and `requestid` correlates with server logs for debugging. |
Error
{
"code": "validation_failed",
"message": "Email has already been taken",
"details": {},
"request_id": "req_01HX9F3K7B2N4P8Q1R6T8Y2Z5"
}
A single error inside an ErrorResponse envelope. code is the
stable machine-readable identifier (snakecase), message is
human-readable, details carries optional structured context
(e.g. validation field breakdown), and `requestid` correlates
with server logs for debugging.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| code | string | true | none | Stable machine-readable error code (snake_case). |
| message | string | true | none | Human-readable error description. |
| details | object¦null | false | none | Optional structured context about the failure. |
| request_id | string | false | none | Correlates with server logs; include when reporting bugs. |
InsufficientScopeResponse
{
"error": "insufficient_scope",
"error_description": "The access token does not include the required scope.",
"scope": "balance"
}
RFC 6750 §3.1 insufficient_scope error. Returned with HTTP 403 and
a WWW-Authenticate: Bearer error="insufficient_scope", scope="..."
header when an OAuth access token authenticates successfully but does
not carry the scope the endpoint requires. Only OAuth bearer tokens
are scope-gated; wk_-prefixed API keys are unscoped and never see
this response.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| error | string | true | none | none |
| error_description | string | true | none | none |
| scope | string | true | none | Space-delimited list of scopes the endpoint requires. |
Enumerated Values
| Property | Value |
|---|---|
| error | insufficient_scope |
MessageResponse
{
"data": {
"message": "string"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | object | true | none | none |
| » message | string | true | none | none |
ResourceEnrollmentRequest
{
"kind": "agent",
"name": "Self-Enrolled Bot",
"description": "string",
"wallet_address": "string"
}
slug and category are accepted but silently ignored — the server
generates the slug from name, and category is a post-claim concern.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| kind | string | true | none | Resource kind. Must be one of the supported kinds (currentlyagent); an unknown value is rejected with a 422. |
| name | string | true | none | Human-readable name; the server derives the slug from it. |
| description | string | false | none | none |
| wallet_address | string | false | none | Declared EVM wallet address for the ghost resource. Stored as a claim (unverified) until the resource is claimed. |
ResourceEnrollmentResponse
{
"data": {
"id": 0,
"name": "string",
"slug": "string",
"description": "string",
"kind": "agent",
"visibility": "public_profile",
"wallet_address": "string",
"category": "string",
"verified": true,
"claimed": true,
"created_at": "2019-08-24T14:15:22Z",
"stats": {
"total_paid": 0,
"total_received": 0,
"payments_made": 0,
"payments_received": 0,
"unique_counterparties": 0,
"first_seen": "2019-08-24T14:15:22Z",
"last_active": "2019-08-24T14:15:22Z"
},
"claim_token": "string",
"claim_url": "string"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | EnrolledResource | true | none | The :created view of a ghost resource. Adds stats, the single-useclaim_token, and the claim_url to the base resource shape. Theclaim fields appear ONLY on this enrollment response — never in list or read views. |
EnrolledResource
{
"id": 0,
"name": "string",
"slug": "string",
"description": "string",
"kind": "agent",
"visibility": "public_profile",
"wallet_address": "string",
"category": "string",
"verified": true,
"claimed": true,
"created_at": "2019-08-24T14:15:22Z",
"stats": {
"total_paid": 0,
"total_received": 0,
"payments_made": 0,
"payments_received": 0,
"unique_counterparties": 0,
"first_seen": "2019-08-24T14:15:22Z",
"last_active": "2019-08-24T14:15:22Z"
},
"claim_token": "string",
"claim_url": "string"
}
The :created view of a ghost resource. Adds stats, the single-use
claim_token, and the claim_url to the base resource shape. The
claim fields appear ONLY on this enrollment response — never in list
or read views.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | integer | true | none | none |
| name | string | true | none | none |
| slug | string | true | none | none |
| description | string¦null | false | none | none |
| kind | string | true | none | none |
| visibility | string | true | none | none |
| wallet_address | string¦null | false | none | Declared wallet address, or null if none was provided. |
| category | string¦null | true | none | Legacy v3 taxonomy field. Returns a string only for agent-kindresources that carry a category in their metadata; null forall other kinds and for agents without one. Always null onenrollment (category is a post-claim concern). |
| verified | boolean | true | none | none |
| claimed | boolean | true | none | True once the resource has an owning provider. Always false at enrollment. |
| created_at | string(date-time) | true | none | none |
| stats | ResourceStats | true | none | Aggregate public payment stats for the resource. |
| claim_token | string | true | none | Single-use token for the magic claim URL. Surfaced only on enrollment. |
| claim_url | string¦null | true | none | Dashboard path the agent hands to its human to claim the resource. |
ResourceStats
{
"total_paid": 0,
"total_received": 0,
"payments_made": 0,
"payments_received": 0,
"unique_counterparties": 0,
"first_seen": "2019-08-24T14:15:22Z",
"last_active": "2019-08-24T14:15:22Z"
}
Aggregate public payment stats for the resource.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| total_paid | integer | true | none | Total atomic units paid out by this resource. |
| total_received | integer | true | none | Total atomic units received by this resource. |
| payments_made | integer | true | none | none |
| payments_received | integer | true | none | none |
| unique_counterparties | integer | true | none | none |
| first_seen | string(date-time)¦null | true | none | none |
| last_active | string(date-time)¦null | true | none | none |
Pagination
{
"page": 0,
"per_page": 0,
"total": 0,
"total_pages": 0
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| page | integer | true | none | none |
| per_page | integer | true | none | none |
| total | integer | true | none | none |
| total_pages | integer | true | none | none |
SignUpRequest
{
"email": "user@example.com",
"password": "string",
"password_confirmation": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| string(email) | true | none | none | |
| password | string | true | none | none |
| password_confirmation | string | true | none | none |
SignInRequest
{
"email": "user@example.com",
"password": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| string(email) | true | none | none | |
| password | string | true | none | none |
ConfirmRequest
{
"confirmation_token": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| confirmation_token | string | true | none | none |
ResendConfirmationRequest
{
"email": "user@example.com"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| string(email) | true | none | none |
PasswordResetRequest
{
"email": "user@example.com"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| string(email) | true | none | none |
PasswordUpdateRequest
{
"reset_password_token": "string",
"password": "string",
"password_confirmation": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| resetpasswordtoken | string | true | none | none |
| password | string | true | none | none |
| password_confirmation | string | true | none | none |
AuthResponse
{
"data": {
"user": {
"id": 0,
"email": "user@example.com",
"status": "string"
},
"token": "string",
"confirmation_required": true
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | object | true | none | none |
| » user | User | true | none | none |
| » token | string¦null | false | none | none |
| » confirmation_required | boolean | true | none | none |
User
{
"id": 0,
"email": "user@example.com",
"status": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | integer | true | none | none |
| string(email) | true | none | none | |
| status | string | false | none | none |
MeResponse
{
"data": {
"principal_type": "organization",
"id": 0,
"name": "string",
"slug": "string",
"kind": "string",
"api_key": {
"id": 0,
"name": "string",
"created_at": "2019-08-24T14:15:22Z",
"last_used_at": "2019-08-24T14:15:22Z",
"created_by": {
"id": 0,
"email": "user@example.com",
"display_name": "string"
}
}
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | any | true | none | none |
oneOf
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | AccountDetails | false | none | The Organization that owns the authenticated API key — the principal in API v1 (the key represents an Org, not a person). api_key carriesaudit info about the key itself, including the user who minted it ( created_by, which may be null if that user has left the Org). |
xor
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | UserPrincipal | false | none | The buyer User represented by an account API key or OAuth token. |
AccountDetails
{
"principal_type": "organization",
"id": 0,
"name": "string",
"slug": "string",
"kind": "string",
"api_key": {
"id": 0,
"name": "string",
"created_at": "2019-08-24T14:15:22Z",
"last_used_at": "2019-08-24T14:15:22Z",
"created_by": {
"id": 0,
"email": "user@example.com",
"display_name": "string"
}
}
}
The Organization that owns the authenticated API key — the principal
in API v1 (the key represents an Org, not a person). api_key carries
audit info about the key itself, including the user who minted it
(created_by, which may be null if that user has left the Org).
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| principal_type | string | true | none | none |
| id | integer | true | none | none |
| name | string | true | none | none |
| slug | string | true | none | none |
| kind | string | true | none | none |
| api_key | MeApiKey | true | none | none |
UserPrincipal
{
"principal_type": "user",
"id": 0,
"email": "user@example.com",
"display_name": "string",
"status": "active",
"buyer_enabled": true,
"seller_enabled": true,
"provisioning_status": "pending",
"wallet": {
"address": "string",
"network": "string"
}
}
The buyer User represented by an account API key or OAuth token.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| principal_type | string | true | none | none |
| id | integer | true | none | none |
| string(email) | true | none | none | |
| display_name | string¦null | false | none | none |
| status | string | true | none | none |
| buyer_enabled | boolean | true | none | none |
| seller_enabled | boolean | true | none | none |
| provisioning_status | string | true | none | none |
| wallet | any | true | none | none |
anyOf
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | PrincipalWallet | false | none | none |
or
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | null | false | none | none |
Enumerated Values
| Property | Value |
|---|---|
| status | active |
| status | blocked |
| provisioning_status | pending |
| provisioning_status | ready |
| provisioning_status | not_applicable |
PrincipalWallet
{
"address": "string",
"network": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| address | string | true | none | none |
| network | string | true | none | none |
MeApiKey
{
"id": 0,
"name": "string",
"created_at": "2019-08-24T14:15:22Z",
"last_used_at": "2019-08-24T14:15:22Z",
"created_by": {
"id": 0,
"email": "user@example.com",
"display_name": "string"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | integer | true | none | none |
| name | string¦null | false | none | none |
| created_at | string(date-time) | true | none | none |
| lastusedat | string(date-time)¦null | false | none | none |
| created_by | MeApiKeyCreator | false | none | The user who minted this API key, surfaced for audit rendering only.null if that user has since left the Organization. NEVER use forauthorization. |
MeApiKeyCreator
{
"id": 0,
"email": "user@example.com",
"display_name": "string"
}
The user who minted this API key, surfaced for audit rendering only.
null if that user has since left the Organization. NEVER use for
authorization.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | integer | true | none | none |
| string(email) | true | none | none | |
| display_name | string¦null | false | none | none |
CreateApiKeyRequest
{
"name": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| name | string | false | none | A friendly name for the API key |
ApiKey
{
"id": 0,
"name": "string",
"key_prefix": "string",
"last_used_at": "2019-08-24T14:15:22Z",
"revoked_at": "2019-08-24T14:15:22Z",
"created_at": "2019-08-24T14:15:22Z"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | integer | true | none | none |
| name | string¦null | false | none | none |
| key_prefix | string | true | none | First 16 characters of the key for identification |
| lastusedat | string(date-time)¦null | false | none | none |
| revoked_at | string(date-time)¦null | false | none | none |
| created_at | string(date-time) | true | none | none |
ApiKeyCreated
{
"id": 0,
"name": "string",
"key_prefix": "string",
"raw_key": "string",
"created_at": "2019-08-24T14:15:22Z"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | integer | true | none | none |
| name | string¦null | false | none | none |
| key_prefix | string | true | none | none |
| raw_key | string | true | none | Full API key (shown only once at creation) |
| created_at | string(date-time) | true | none | none |
ApiKeyListResponse
{
"data": [
{
"id": 0,
"name": "string",
"key_prefix": "string",
"last_used_at": "2019-08-24T14:15:22Z",
"revoked_at": "2019-08-24T14:15:22Z",
"created_at": "2019-08-24T14:15:22Z"
}
],
"pagination": {
"page": 0,
"per_page": 0,
"total": 0,
"total_pages": 0
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | [ApiKey] | true | none | none |
| pagination | Pagination | true | none | none |
ApiKeyCreatedResponse
{
"data": {
"id": 0,
"name": "string",
"key_prefix": "string",
"raw_key": "string",
"created_at": "2019-08-24T14:15:22Z"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | ApiKeyCreated | true | none | none |
Payment
{
"id": 0,
"tx_hash": "string",
"payer_address": "string",
"recipient_address": "string",
"amount": 0,
"amount_formatted": "string",
"currency": "string",
"network": "string",
"resource_url": "string",
"resource_host": "string",
"settlement_latency_ms": 0,
"settled_at": "2019-08-24T14:15:22Z",
"api_key_name": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | integer | true | none | none |
| tx_hash | string | true | none | none |
| payer_address | string | true | none | none |
| recipient_address | string | true | none | none |
| amount | integer | true | none | Amount in atomic units (1 USDC = 1,000,000) |
| amount_formatted | string | true | none | Human-readable amount (e.g. "1.00 USDC") |
| currency | string | true | none | none |
| network | string | true | none | CAIP-2 chain identifier |
| resource_url | string¦null | false | none | none |
| resource_host | string¦null | false | none | none |
| settlementlatencyms | integer¦null | false | none | none |
| settled_at | string(date-time) | true | none | none |
| apikeyname | string | true | none | none |
PaymentListResponse
{
"data": [
{
"id": 0,
"tx_hash": "string",
"payer_address": "string",
"recipient_address": "string",
"amount": 0,
"amount_formatted": "string",
"currency": "string",
"network": "string",
"resource_url": "string",
"resource_host": "string",
"settlement_latency_ms": 0,
"settled_at": "2019-08-24T14:15:22Z",
"api_key_name": "string"
}
],
"pagination": {
"page": 0,
"per_page": 0,
"total": 0,
"total_pages": 0
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | [Payment] | true | none | none |
| pagination | Pagination | true | none | none |
PaymentResponse
{
"data": {
"id": 0,
"tx_hash": "string",
"payer_address": "string",
"recipient_address": "string",
"amount": 0,
"amount_formatted": "string",
"currency": "string",
"network": "string",
"resource_url": "string",
"resource_host": "string",
"settlement_latency_ms": 0,
"settled_at": "2019-08-24T14:15:22Z",
"api_key_name": "string"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | Payment | true | none | none |
Purchase
{
"id": 0,
"purchase_id": "pur_4V3n9mQ0b7XyK2sLpR8tWc1d",
"status": "signed",
"amount_usd": "string",
"recipient_address": "string",
"network": "string",
"protocol": "mpp",
"context": "string",
"search_id": "string",
"attribution_status": "attributed",
"tx_hash": "string",
"reject_reason": "string",
"failure_reason": "string",
"idempotency_key": "string",
"signed_at": "2019-08-24T14:15:22Z",
"settled_at": "2019-08-24T14:15:22Z",
"artifact": {
"id": 0,
"merchant_url": "http://example.com",
"bytes": 0,
"mime": "string"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | integer | true | none | none |
| purchase_id | string | false | none | Opaque purchase id: the value fetch returns as purchase_id andthat GET /api/v1/purchases/{id} accepts. Prefer it over thesequential id. Current servers always send this field; it isoptional in this schema so that a client also accepts a response from an older server. |
| status | string | true | none | none |
| amount_usd | string | true | none | Exact decimal USD amount with up to six fractional digits. Settled rows report the amount that moved; pending and failed rows report their authorization amount. |
| recipient_address | string | true | none | none |
| network | string | true | none | none |
| protocol | string,null¦null | true | none | none |
| context | string,null¦null | true | none | none |
| search_id | string,null¦null | true | none | Search trace ID used to attribute this purchase, when available. |
| attribution_status | string | true | none | none |
| tx_hash | string,null¦null | true | none | none |
| reject_reason | string,null¦null | true | none | none |
| failure_reason | string,null¦null | true | none | none |
| idempotency_key | string,null¦null | true | none | none |
| signed_at | string(date-time) | true | none | none |
| settled_at | string,null(date-time)¦null | true | none | none |
| artifact | any | true | none | none |
anyOf
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | PurchaseArtifact | false | none | none |
or
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | null | false | none | none |
Enumerated Values
| Property | Value |
|---|---|
| status | signed |
| status | settled |
| status | locked |
| status | rejected |
| status | declined |
| status | expired |
| status | reverted |
| protocol | mpp |
| protocol | x402 |
| protocol | null |
| attribution_status | attributed |
| attribution_status | unattributed |
PurchaseArtifact
{
"id": 0,
"merchant_url": "http://example.com",
"bytes": 0,
"mime": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | integer | true | none | none |
| merchant_url | string(uri) | true | none | none |
| bytes | integer | true | none | none |
| mime | string,null¦null | true | none | none |
PurchaseListResponse
{
"data": [
{
"id": 0,
"purchase_id": "pur_4V3n9mQ0b7XyK2sLpR8tWc1d",
"status": "signed",
"amount_usd": "string",
"recipient_address": "string",
"network": "string",
"protocol": "mpp",
"context": "string",
"search_id": "string",
"attribution_status": "attributed",
"tx_hash": "string",
"reject_reason": "string",
"failure_reason": "string",
"idempotency_key": "string",
"signed_at": "2019-08-24T14:15:22Z",
"settled_at": "2019-08-24T14:15:22Z",
"artifact": {
"id": 0,
"merchant_url": "http://example.com",
"bytes": 0,
"mime": "string"
}
}
],
"pagination": {
"page": 0,
"per_page": 0,
"total": 0,
"total_pages": 0
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | [Purchase] | true | none | none |
| pagination | Pagination | true | none | none |
PurchaseResponse
{
"data": {
"id": 0,
"purchase_id": "pur_4V3n9mQ0b7XyK2sLpR8tWc1d",
"status": "signed",
"amount_usd": "string",
"recipient_address": "string",
"network": "string",
"protocol": "mpp",
"context": "string",
"search_id": "string",
"attribution_status": "attributed",
"tx_hash": "string",
"reject_reason": "string",
"failure_reason": "string",
"idempotency_key": "string",
"signed_at": "2019-08-24T14:15:22Z",
"settled_at": "2019-08-24T14:15:22Z",
"artifact": {
"id": 0,
"merchant_url": "http://example.com",
"bytes": 0,
"mime": "string"
}
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| data | Purchase | true | none | none |
BalanceResponse
{
"promo": {
"balance_usd": "0.00",
"scope": "weft-search",
"expires_at": "2019-08-24T14:15:22Z"
},
"wallet": {
"provider": "crossmint",
"address": "string",
"balance_usdc": "12.34",
"balance_tempo_usd": "2.03",
"total_usd": "12.34",
"network": "string"
},
"spent_today_usd": "0.0005",
"spent_week_usd": "3.10",
"policy_used_today_usd": "0.30",
"policy_used_week_usd": "3.30",
"policy": {
"max_tx_usd": "1.00",
"daily_limit_usd": "10.00",
"weekly_limit_usd": "50.00"
}
}
Buyer's wallet snapshot. Note: unlike /me / /payments / /api_keys,
this endpoint does NOT use a data: envelope — the response object
is the snapshot directly.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| promo | PromoBalance | true | none | Freemium promo credit. v1 returns zero values; once the promo ledger ships (deferred from spec 10), real balances appear here without a shape change. |
| wallet | Wallet | true | none | none |
| spenttodayusd | string | true | none | USD spent in the current calendar day (UTC). Exact to the micro-dollar so sub-cent micro-payments survive ("0.0005", "0.42"), never narrower than two decimals; a zero total renders as "0.00". |
| spentweekusd | string | true | none | USD spent in the current calendar week (UTC, Monday start). Exact to the micro-dollar ("0.0005", "3.10"), never narrower than two decimals; a zero total renders as "0.00". |
| policyusedtoday_usd | string | true | none | USD currently counted against the daily spending limit: pending authorizations at their maximum plus settled payments at the amount that moved. Exact to the micro-dollar; zero renders as "0.00". |
| policyusedweek_usd | string | true | none | USD currently counted against the weekly spending limit: pending authorizations at their maximum plus settled payments at the amount that moved. Exact to the micro-dollar; zero renders as "0.00". |
| policy | SpendingPolicy | true | none | none |
PromoBalance
{
"balance_usd": "0.00",
"scope": "weft-search",
"expires_at": "2019-08-24T14:15:22Z"
}
Freemium promo credit. v1 returns zero values; once the promo ledger ships (deferred from spec 10), real balances appear here without a shape change.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| balance_usd | string | true | none | none |
| scope | string | true | none | Where the promo can be spent. |
| expires_at | string(date-time)¦null | true | none | none |
Wallet
{
"provider": "crossmint",
"address": "string",
"balance_usdc": "12.34",
"balance_tempo_usd": "2.03",
"total_usd": "12.34",
"network": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| provider | string | true | none | Crossmint is the only buyer-wallet provider. |
| address | string¦null | true | none | Base smart-wallet address. Null only when Crossmint is unavailable. |
| balance_usdc | string¦null | true | none | Live Base USDC balance, exact to the micro-dollar (up to 6 decimals, minimum 2). Null when Crossmint is unreachable; consumers must not interpret null as zero. |
| balancetempousd | string¦null | true | none | Live aggregate Tempo dollar-token balance, exact to the micro-dollar (up to 6 decimals, minimum 2). Null when Crossmint is unreachable; consumers must not interpret null as zero. |
| total_usd | string¦null | true | none | Single aggregated USD balance across Base USDC and Tempo dollar tokens, exact to the micro-dollar. Null when either pocket is unreachable, because the surface never claims zero for a component it could not read. |
| network | string¦null | true | none | Selected Crossmint environment (base_sepolia or base_mainnet). |
Enumerated Values
| Property | Value |
|---|---|
| provider | crossmint |
SpendingPolicy
{
"max_tx_usd": "1.00",
"daily_limit_usd": "10.00",
"weekly_limit_usd": "50.00"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| maxtxusd | string | true | none | Maximum USD per single transaction. |
| dailylimitusd | string | true | none | Maximum USD spent in a 24-hour window. |
| weeklylimitusd | string | true | none | Maximum USD spent in a 7-day window. |
SearchRequest
{
"query": "send email from an agent",
"max_results": 10,
"filters": {
"price": {
"lte": "string",
"gte": "string",
"eq": "string",
"range_gte": "string",
"range_lte": "string"
},
"price_atomic": {
"lte": 0,
"gte": 0,
"eq": 0,
"range_gte": 0,
"range_lte": 0
},
"type": {
"eq": "api",
"in": [
"api"
]
},
"protocol": {
"eq": "x402",
"in": [
"x402"
]
},
"category": {
"eq": "string",
"in": [
"string"
]
},
"method": {
"eq": "GET",
"in": [
"GET"
]
},
"execution_mode": {
"eq": "sync",
"in": [
"sync"
]
},
"weft_fetch_compatible": true,
"include_unknown_prices": false
},
"view": "full"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| query | string | true | none | Free-text query. Required and non-empty. |
| max_results | integer | false | none | Max number of hits to return. Invalid values are rejected, not clamped. |
| filters | SearchFilterSpec | false | none | Structured hard constraints on the query — the weft-search-platformFilterSpec v1 typed subset, vendored verbatim (D1 contract). Everyfield is optional; an empty object is the no-filter query. Set fields are ANDed. Each sub-filter takes exactly one operator; unknown filter keys and unknown operators are rejected ( 422 INVALID_FILTERS).price (USD decimal strings) and price_atomic (integer micro-USD) aretwo representations of ONE price constraint — the reasoning form and the settlement form. They are MUTUALLY EXCLUSIVE: setting both is 422 INVALID_FILTERS. A JSON number in price is rejected (only decimalstrings), so {lte: 5} can never be mis-read as 5 micro-dollars.The canonical enum value sets are pinned to the platform spec by the CI drift gate ( docs/contracts/search-platform.openapi.yaml). |
| view | string | false | none | Response view. Absent means full: the complete SearchResultrows in results. compact returns the hosted MCP agent view incompact_results (see SearchCompactResult). The compact viewapplies the same selection as hosted MCP search: it leaves out incomplete PayWithLocus gateway contracts. It does not add filters; send weft_fetch_compatible: true to get only results thatPOST /api/v1/fetch can pay. |
Enumerated Values
| Property | Value |
|---|---|
| view | full |
| view | compact |
SearchFilterSpec
{
"price": {
"lte": "string",
"gte": "string",
"eq": "string",
"range_gte": "string",
"range_lte": "string"
},
"price_atomic": {
"lte": 0,
"gte": 0,
"eq": 0,
"range_gte": 0,
"range_lte": 0
},
"type": {
"eq": "api",
"in": [
"api"
]
},
"protocol": {
"eq": "x402",
"in": [
"x402"
]
},
"category": {
"eq": "string",
"in": [
"string"
]
},
"method": {
"eq": "GET",
"in": [
"GET"
]
},
"execution_mode": {
"eq": "sync",
"in": [
"sync"
]
},
"weft_fetch_compatible": true,
"include_unknown_prices": false
}
Structured hard constraints on the query — the weft-search-platform
FilterSpec v1 typed subset, vendored verbatim (D1 contract). Every
field is optional; an empty object is the no-filter query. Set fields
are ANDed. Each sub-filter takes exactly one operator; unknown filter
keys and unknown operators are rejected (422 INVALID_FILTERS).
price (USD decimal strings) and price_atomic (integer micro-USD) are
two representations of ONE price constraint — the reasoning form and the
settlement form. They are MUTUALLY EXCLUSIVE: setting both is
422 INVALID_FILTERS. A JSON number in price is rejected (only decimal
strings), so {lte: 5} can never be mis-read as 5 micro-dollars.
The canonical enum value sets are pinned to the platform spec by the CI
drift gate (docs/contracts/search-platform.openapi.yaml).
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| price | SearchPriceUsdFilter | false | none | Price constraint in USD, as decimal STRINGS (the reasoning form). Set exactly one operator: a scalar ( lte / gte / eq) OR a range( range_gte + range_lte together, lower ≤ upper). Each value is astring matching ^\d+(\.\d{1,6})?$ (≤ 6 decimals, ≥ 0); a JSON number isrejected. Mutually exclusive with price_atomic. An unpriced resource isexcluded from any price-filtered query. |
| price_atomic | SearchPriceAtomicFilter | false | none | The same price constraint in integer micro-USD (1 USDC = 1000000 micro-USD) — the settlement form. Set exactly one operator: a scalar ( lte / gte / eq) OR a range (range_gte + range_lte together,lower ≤ upper). Mutually exclusive with price. An unpriced resource isexcluded from any price-filtered query. |
| type | SearchResourceTypeFilter | false | none | Resource-type constraint. Set exactly one of eq / in. |
| protocol | SearchProtocolFilter | false | none | Payment-protocol constraint. Set exactly one of eq / in. |
| category | SearchStringSetFilter | false | none | none |
| method | SearchMethodFilter | false | none | none |
| execution_mode | SearchExecutionModeFilter | false | none | none |
| weftfetchcompatible | boolean | false | none | True requires at least one access method whose paid request is supported by the current Weft fetch runtime. For async operations this can mean submission-only coverage followed by the provider workflow. |
| includeunknownprices | boolean | false | none | With a price constraint, also retain dynamic and unknown prices. |
SearchPriceUsdFilter
{
"lte": "string",
"gte": "string",
"eq": "string",
"range_gte": "string",
"range_lte": "string"
}
Price constraint in USD, as decimal STRINGS (the reasoning form). Set
exactly one operator: a scalar (lte / gte / eq) OR a range
(range_gte + range_lte together, lower ≤ upper). Each value is a
string matching ^\d+(\.\d{1,6})?$ (≤ 6 decimals, ≥ 0); a JSON number is
rejected. Mutually exclusive with price_atomic. An unpriced resource is
excluded from any price-filtered query.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| lte | string | false | none | Price ≤ this many USD (inclusive ceiling), e.g. "0.009". |
| gte | string | false | none | Price ≥ this many USD (inclusive floor), e.g. "0.001". |
| eq | string | false | none | Price exactly this many USD, e.g. "1.5". |
| range_gte | string | false | none | Range lower bound (inclusive), USD. Set with range_lte. |
| range_lte | string | false | none | Range upper bound (inclusive), USD. Set with range_gte. |
SearchPriceAtomicFilter
{
"lte": 0,
"gte": 0,
"eq": 0,
"range_gte": 0,
"range_lte": 0
}
The same price constraint in integer micro-USD (1 USDC = 1000000
micro-USD) — the settlement form. Set exactly one operator: a scalar
(lte / gte / eq) OR a range (range_gte + range_lte together,
lower ≤ upper). Mutually exclusive with price. An unpriced resource is
excluded from any price-filtered query.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| lte | integer | false | none | Price ≤ this many micro-USD (inclusive ceiling). |
| gte | integer | false | none | Price ≥ this many micro-USD (inclusive floor). |
| eq | integer | false | none | Price exactly this many micro-USD. |
| range_gte | integer | false | none | Range lower bound (inclusive), micro-USD. Set with range_lte. |
| range_lte | integer | false | none | Range upper bound (inclusive), micro-USD. Set with range_gte. |
SearchResourceTypeFilter
{
"eq": "api",
"in": [
"api"
]
}
Resource-type constraint. Set exactly one of eq / in.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| eq | string | false | none | Resource type is exactly this value. |
| in | [string] | false | none | Resource type is one of these values. |
Enumerated Values
| Property | Value |
|---|---|
| eq | api |
| eq | agent |
| eq | mcp_tool |
| eq | webpage |
| eq | feed |
| eq | document |
| eq | dataset |
SearchProtocolFilter
{
"eq": "x402",
"in": [
"x402"
]
}
Payment-protocol constraint. Set exactly one of eq / in.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| eq | string | false | none | Primary protocol is exactly this value. |
| in | [string] | false | none | Primary protocol is one of these values. |
Enumerated Values
| Property | Value |
|---|---|
| eq | x402 |
| eq | a2a |
| eq | mpp |
| eq | erc_8004 |
SearchStringSetFilter
{
"eq": "string",
"in": [
"string"
]
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| eq | string | false | none | none |
| in | [string] | false | none | none |
SearchMethodFilter
{
"eq": "GET",
"in": [
"GET"
]
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| eq | string | false | none | none |
| in | [string] | false | none | none |
Enumerated Values
| Property | Value |
|---|---|
| eq | GET |
| eq | POST |
| eq | PUT |
| eq | PATCH |
| eq | DELETE |
| eq | HEAD |
| eq | OPTIONS |
SearchExecutionModeFilter
{
"eq": "sync",
"in": [
"sync"
]
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| eq | string | false | none | none |
| in | [string] | false | none | none |
Enumerated Values
| Property | Value |
|---|---|
| eq | sync |
| eq | async |
| eq | stream |
SearchResponse
{
"query_trace_id": "2e536172-f35a-49b0-b5b1-ca3970d0f446",
"query": "string",
"format": "json",
"markdown": "string",
"result_count": 0,
"served_from": "live",
"as_of": "2019-08-24T14:15:22Z",
"stale": true,
"payment_note": "string",
"applied_filters": {
"price": {
"lte": "string",
"gte": "string",
"eq": "string",
"range_gte": "string",
"range_lte": "string"
},
"price_atomic": {
"lte": 0,
"gte": 0,
"eq": 0,
"range_gte": 0,
"range_lte": 0
},
"type": {
"eq": "api",
"in": [
"api"
]
},
"protocol": {
"eq": "x402",
"in": [
"x402"
]
},
"category": {
"eq": "string",
"in": [
"string"
]
},
"method": {
"eq": "GET",
"in": [
"GET"
]
},
"execution_mode": {
"eq": "sync",
"in": [
"sync"
]
},
"weft_fetch_compatible": true,
"include_unknown_prices": false
},
"decomposition_source": "CALLER",
"embedder_model": "string",
"candidates_considered": 0,
"warnings": [
{
"code": "string",
"message": "string",
"cause": "string",
"context": {}
}
],
"match_quality": "strong",
"reason": "below_relevance_floor",
"suggestion": "string",
"results": [
{
"provider": {
"provider_id": "string",
"display_name": "string",
"origin_domains": [
"string"
]
},
"capability": {
"capability_id": "string",
"capability_type": "string"
},
"endpoints": [
{
"endpoint_id": "string",
"url": "string",
"resource_type": "string",
"primary_protocol": "string",
"call": {
"method": "string",
"input_schema": {},
"example_request": {},
"example_response": null
},
"price": {
"indexed_usd": "string",
"atomic": 0,
"source": "probe",
"last_observed_at": "2019-08-24T14:15:22Z",
"live_verified": true
},
"payment": [
{
"protocol": "string",
"scheme": "string",
"network": "string",
"asset": "string",
"amount": "string",
"pay_to": "string",
"max_timeout_seconds": 0,
"facilitator": "string"
}
],
"access_methods": [
{
"access_method_id": "string",
"protocol": "x402",
"scheme": "string",
"network": "string",
"asset": "string",
"asset_decimals": 0,
"price": {},
"merchant": {},
"terms": {},
"status": "listed",
"weft_fetch": {
"state": "supported",
"reason": "string",
"contract_version": 0,
"coverage": "terminal_response"
}
}
],
"service": {
"id": "string",
"name": "string"
},
"operation": {
"id": "string",
"name": "string",
"description": "string"
},
"docs": {
"website": "http://example.com",
"homepage": "http://example.com",
"api": "http://example.com",
"llms_txt": "http://example.com"
},
"contract_url": "http://example.com",
"output_schema": {},
"output": {},
"execution": {
"mode": "sync"
},
"callability": {
"state": "complete",
"missing": [
"string"
]
},
"compatibility": {},
"source": {
"catalog": "curated_marketplace",
"snapshot_version": 0,
"captured_at": "2019-08-24T14:15:22Z"
},
"operator_type": "first_party",
"operated_by_id": "string",
"settled_via_facilitator_id": "string",
"settlements": 0,
"last_verified_at": "2019-08-24T14:15:22Z",
"latency_p50_ms": 0
}
],
"score": 0
}
],
"view": "full",
"compact_results": [
{
"provider": {
"id": "string",
"name": "string"
},
"operation": {
"id": "string",
"name": "string",
"description": "string"
},
"inputs": {},
"request": {},
"example": null,
"access": {
"id": "string",
"protocol": "string",
"weft_fetch": {},
"price": {
"kind": "string",
"indexed_usd": "string",
"unit": "string",
"hint": "string"
}
},
"attribution": {
"search_id": "d5826b15-4ef0-45f4-80df-3b8d5f941dd6",
"operation_id": "string",
"access_method_id": "string"
},
"output": {},
"docs": {
"website": "http://example.com",
"homepage": "http://example.com",
"api": "http://example.com",
"llms_txt": "http://example.com"
},
"contract_url": "http://example.com",
"contract_detail": "string",
"completion": "string",
"blocked_scopes": [
{
"scope": "string",
"state": "string"
}
],
"contract_gaps": [
"string"
],
"readiness": {
"property1": {
"state": "string",
"scope": "string"
},
"property2": {
"state": "string",
"scope": "string"
}
},
"next_call": {
"method": "POST",
"path": "/api/v1/fetch",
"body": {},
"fill": [
"string"
]
}
}
],
"omitted_results": 1
}
The weft-search-platform POST /v1/search response envelope.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| querytraceid | string(uuid) | true | none | Opaque trace id for the served query, matching the platformquery_trace_id — and the search_id that re-reads it. Deliberatelythe same id rather than a second handle: one serve, one name. |
| query | string | true | none | none |
| format | string | false | none | Which view this response carries, echoing the requested format.json populates results and leaves markdown null; markdownpopulates markdown and leaves results empty. Exactly one ispopulated per response, never both — returning both eagerly would double the payload. |
| markdown | string¦null | false | none | The flat comparison table, when format is markdown; nullotherwise. Rendered deterministically from the same stored result the json view returns, so the two views of one search can neverdisagree. |
| result_count | integer | false | none | How many (Provider + Capability) results the search produced. ReadTHIS, not results.length, for the count: on a markdown responseresults is empty by design, so result_count > 0 with an emptyresults is a rendered table while result_count == 0 is a genuinezero-result. On a compact view response it countscompact_results; there, result_count == 0 withomitted_results means every ranked result was too large to show. |
| served_from | string | false | none | live for a fresh serve; snapshot for an immutable replay of acompleted search. A re-read is a snapshot and says so — read it with as_of and stale. |
| as_of | string(date-time)¦null | false | none | When the results were produced. Now on a live serve; on asnapshot the ORIGINAL serve time, because a replay returns exactlywhat was paid for, not current truth. |
| stale | boolean | false | none | Whether the active search index has changed since these results were produced. true does not mean the results are wrong — it means theyare a snapshot of an index that has since moved. Always false on alive serve. |
| payment_note | string | false | none | How payment works across the catalog, stated once for the whole response rather than repeated in every endpoint's usage instructions. |
| applied_filters | SearchFilterSpec | false | none | The FilterSpec actually applied to recall, echoed back so thecaller sees exactly what constrained the results. In the current contract this is the caller's filters verbatim (empty object whennone were sent). |
| decomposition_source | string | false | none | Origin of applied_filters; always CALLER. |
| embedder_model | string | true | none | none |
| candidates_considered | integer | true | none | none |
| warnings | [object] | true | none | none |
| » code | string | true | none | none |
| » message | string | true | none | none |
| » cause | string¦null | false | none | Typed cause for codes that carry one. Null otherwise. |
| » context | object¦null | false | none | Structured context for the warning. Null when the code carries none. |
| match_quality | string | false | none | Calibrated confidence in this result set — read this instead of thresholding a raw similarity score. strong: the top match isconfidently on-intent, proceed. weak: above the relevance floorbut inside the band where plausible-but-wrong matches live, verify the result before paying. none: nothing cleared the floor —results is empty and reason / suggestion say why. Resultsbelow the floor are never returned as near-misses. |
| reason | string¦null | false | none | Machine-readable cause of an empty result set, non-null exactly whenmatch_quality is none. below_relevance_floor: candidates rankedbut the best scored under the floor. filter_collapsed_pool: a callerfilter cut the pool before ranking and nothing relevant survived — relax the filter. no_catalog_coverage: recall returned no candidatesat all on an unfiltered query. unsupported_filter_value: aCATEGORICAL filter value ( type / protocol) matches zero storedvalues, so it could never have matched — categorical only, since a continuous bound like price that matches nothing is reported asfilter_collapsed_pool instead (the bound needs raising, thedimension is not unsupported). index_unavailable: no active index— an operational fault, paired with the EMPTY_INDEX warning. |
| suggestion | string¦null | false | none | Human/agent-readable explanation of reason, carrying the concretenumbers behind it (pool sizes, the best discarded score against the floor, the values actually stored for a filter). Non-null exactly when reason is. |
| results | [SearchResult] | true | none | Ranked (Provider + Capability) results; empty when the index isempty or nothing cleared the relevance floor. Results BELOW the floor are never returned — an empty list plus a reason is the honestanswer, not a list of near-misses in the same shape as a genuine match. Empty on a compact view response; read compact_results. |
| view | string | false | none | The view this response carries. Present as compact when therequest asked for view: "compact"; absent means full. |
| compact_results | [SearchCompactResult] | false | none | Ranked compact results, present only on a compact view response.result_count is their count. They are a separate field so thattyped clients keep the full SearchResult shape for results. |
| omitted_results | integer | false | none | Compact view only: how many ranked results were left out because even their minimum summary does not fit the per-result budget. Absent when none were left out. |
Enumerated Values
| Property | Value |
|---|---|
| format | json |
| format | markdown |
| served_from | live |
| served_from | snapshot |
| decomposition_source | CALLER |
| match_quality | strong |
| match_quality | weak |
| match_quality | none |
| reason | belowrelevancefloor |
| reason | filtercollapsedpool |
| reason | nocatalogcoverage |
| reason | unsupportedfiltervalue |
| reason | index_unavailable |
| view | full |
| view | compact |
SearchCompactResult
{
"provider": {
"id": "string",
"name": "string"
},
"operation": {
"id": "string",
"name": "string",
"description": "string"
},
"inputs": {},
"request": {},
"example": null,
"access": {
"id": "string",
"protocol": "string",
"weft_fetch": {},
"price": {
"kind": "string",
"indexed_usd": "string",
"unit": "string",
"hint": "string"
}
},
"attribution": {
"search_id": "d5826b15-4ef0-45f4-80df-3b8d5f941dd6",
"operation_id": "string",
"access_method_id": "string"
},
"output": {},
"docs": {
"website": "http://example.com",
"homepage": "http://example.com",
"api": "http://example.com",
"llms_txt": "http://example.com"
},
"contract_url": "http://example.com",
"contract_detail": "string",
"completion": "string",
"blocked_scopes": [
{
"scope": "string",
"state": "string"
}
],
"contract_gaps": [
"string"
],
"readiness": {
"property1": {
"state": "string",
"scope": "string"
},
"property2": {
"state": "string",
"scope": "string"
}
},
"next_call": {
"method": "POST",
"path": "/api/v1/fetch",
"body": {},
"fill": [
"string"
]
}
}
One row of the compact agent view: the minimum contract for the next
paid call, below 2,000 JSON bytes. It is the hosted MCP weft_search
projection with REST references: contract_url replaces the MCP
weft_contract call, and next_call is a POST /api/v1/fetch
request instead of an MCP weft_fetch call. To stay in the budget,
the row drops optional fields in a fixed order (optional inputs, helper
docs, the example, the description, then summaries) and
contract_detail says what is left out. In the last resort the row
keeps the operation id, readiness states, the weft_fetch state,
contract_url and the search id, and can lose the input names, the
request recipe and price. The full contract at contract_url is
always complete. The search id is the
envelope query_trace_id; each row also carries it in attribution
or in next_call.body.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| provider | object | false | none | none |
| » id | string | false | none | none |
| » name | string | false | none | none |
| operation | object | true | none | none |
| » id | string | true | none | Catalog operation id. Send it as operation_id to POST /api/v1/fetch. |
| » name | string | false | none | none |
| » description | string | false | none | none |
| inputs | object | false | none | Typed business inputs (a bounded JSON schema), or the names and locations of the required inputs when the full schema does not fit. Read the full contract before you send an input not listed. |
| request | object | false | none | The request recipe: method, canonical url, and, when known,the path, query, header and JSON body bindings of each input. |
| example | any | false | none | A bounded documented request example, when the catalog has one. |
| access | SearchCompactAccess | true | none | The recommended access method for this operation. |
| attribution | object | false | none | Labels to send with the paid fetch so the purchase links to this search. Left out when next_call.body already carries them. |
| » search_id | string(uuid) | false | none | none |
| » operation_id | string | false | none | none |
| » accessmethodid | string | false | none | none |
| output | object | false | none | Media type, execution mode, a bounded output summary and, for async work, the poll workflow. |
| docs | SearchHelperDocs | false | none | Free helper links for the upstream provider. GET these URLs. Do not send them through weft_fetch. |
| contract_url | string(uri) | true | none | Content-addressed full Weft contract. GET it with your Weft credential (a buyer account key, an OAuth token with the searchscope, or a claimed wbt_* bootstrap bearer) before you build arequest the summary does not fully describe. A pending bootstrap bearer gets 401 until a human approves the account. |
| contract_detail | string | true | none | What this summary leaves out of the full contract. |
| completion | string | false | none | Completion guidance when the completion rules did not fit inline. |
| blocked_scopes | [object] | false | none | none |
| » scope | string | false | none | none |
| » state | string | false | none | none |
| contract_gaps | [string] | false | none | Contract fields that the review did not complete. |
| readiness | object | true | none | Independent readiness claims (research, contract, runtime,paid_terminal), each with a state and, when it fits, a scope. |
| » additionalProperties | object | false | none | none |
| »» state | string | false | none | none |
| »» scope | string | false | none | none |
| next_call | SearchCompactNextCall | false | none | A ready paid call, present when the request is determined and the Weft fetch runtime can pay the access method. Replace each {name}listed in fill in body (and add max_cost_usd or body when theyare listed), then send body as the JSON body of POST /api/v1/fetchwith an Idempotency-Key header. body uses the wire field names( max_cost_usd, search_id); an SDK fetch call may name themdifferently, so map each field and keep the attribution fields. |
SearchCompactAccess
{
"id": "string",
"protocol": "string",
"weft_fetch": {},
"price": {
"kind": "string",
"indexed_usd": "string",
"unit": "string",
"hint": "string"
}
}
The recommended access method for this operation.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | string | false | none | The access_method_id to send to POST /api/v1/fetch. |
| protocol | string | false | none | none |
| weft_fetch | object | false | none | Whether the Weft fetch runtime can pay this access method (state, and reason and coverage when they fit). |
| price | object | false | none | none |
| » kind | string | false | none | Price kind, such as fixed, dynamic, free or unknown. |
| » indexed_usd | string¦null | false | none | Indexed USD price. A dynamic price is not a cap. |
| » unit | string¦null | false | none | none |
| » hint | string | false | none | none |
SearchCompactNextCall
{
"method": "POST",
"path": "/api/v1/fetch",
"body": {},
"fill": [
"string"
]
}
A ready paid call, present when the request is determined and the
Weft fetch runtime can pay the access method. Replace each {name}
listed in fill in body (and add max_cost_usd or body when they
are listed), then send body as the JSON body of POST /api/v1/fetch
with an Idempotency-Key header. body uses the wire field names
(max_cost_usd, search_id); an SDK fetch call may name them
differently, so map each field and keep the attribution fields.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| method | string | true | none | none |
| path | string | true | none | none |
| body | object | true | none | A FetchRequest with documented example values and {name} placeholders. |
| fill | [string] | true | none | Names to replace before you send the request. |
Enumerated Values
| Property | Value |
|---|---|
| method | POST |
| path | /api/v1/fetch |
SearchResult
{
"provider": {
"provider_id": "string",
"display_name": "string",
"origin_domains": [
"string"
]
},
"capability": {
"capability_id": "string",
"capability_type": "string"
},
"endpoints": [
{
"endpoint_id": "string",
"url": "string",
"resource_type": "string",
"primary_protocol": "string",
"call": {
"method": "string",
"input_schema": {},
"example_request": {},
"example_response": null
},
"price": {
"indexed_usd": "string",
"atomic": 0,
"source": "probe",
"last_observed_at": "2019-08-24T14:15:22Z",
"live_verified": true
},
"payment": [
{
"protocol": "string",
"scheme": "string",
"network": "string",
"asset": "string",
"amount": "string",
"pay_to": "string",
"max_timeout_seconds": 0,
"facilitator": "string"
}
],
"access_methods": [
{
"access_method_id": "string",
"protocol": "x402",
"scheme": "string",
"network": "string",
"asset": "string",
"asset_decimals": 0,
"price": {},
"merchant": {},
"terms": {},
"status": "listed",
"weft_fetch": {
"state": "supported",
"reason": "string",
"contract_version": 0,
"coverage": "terminal_response"
}
}
],
"service": {
"id": "string",
"name": "string"
},
"operation": {
"id": "string",
"name": "string",
"description": "string"
},
"docs": {
"website": "http://example.com",
"homepage": "http://example.com",
"api": "http://example.com",
"llms_txt": "http://example.com"
},
"contract_url": "http://example.com",
"output_schema": {},
"output": {},
"execution": {
"mode": "sync"
},
"callability": {
"state": "complete",
"missing": [
"string"
]
},
"compatibility": {},
"source": {
"catalog": "curated_marketplace",
"snapshot_version": 0,
"captured_at": "2019-08-24T14:15:22Z"
},
"operator_type": "first_party",
"operated_by_id": "string",
"settled_via_facilitator_id": "string",
"settlements": 0,
"last_verified_at": "2019-08-24T14:15:22Z",
"latency_p50_ms": 0
}
],
"score": 0
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| provider | SearchProviderRef | true | none | none |
| capability | SearchCapabilityRef | true | none | none |
| endpoints | [SearchEndpointHit] | true | none | none |
| score | number(double) | true | none | none |
SearchProviderRef
{
"provider_id": "string",
"display_name": "string",
"origin_domains": [
"string"
]
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| provider_id | string | false | none | none |
| display_name | string¦null | false | none | none |
| origin_domains | [string] | false | none | none |
SearchCapabilityRef
{
"capability_id": "string",
"capability_type": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| capability_id | string | false | none | none |
| capability_type | string¦null | false | none | none |
SearchEndpointHit
{
"endpoint_id": "string",
"url": "string",
"resource_type": "string",
"primary_protocol": "string",
"call": {
"method": "string",
"input_schema": {},
"example_request": {},
"example_response": null
},
"price": {
"indexed_usd": "string",
"atomic": 0,
"source": "probe",
"last_observed_at": "2019-08-24T14:15:22Z",
"live_verified": true
},
"payment": [
{
"protocol": "string",
"scheme": "string",
"network": "string",
"asset": "string",
"amount": "string",
"pay_to": "string",
"max_timeout_seconds": 0,
"facilitator": "string"
}
],
"access_methods": [
{
"access_method_id": "string",
"protocol": "x402",
"scheme": "string",
"network": "string",
"asset": "string",
"asset_decimals": 0,
"price": {},
"merchant": {},
"terms": {},
"status": "listed",
"weft_fetch": {
"state": "supported",
"reason": "string",
"contract_version": 0,
"coverage": "terminal_response"
}
}
],
"service": {
"id": "string",
"name": "string"
},
"operation": {
"id": "string",
"name": "string",
"description": "string"
},
"docs": {
"website": "http://example.com",
"homepage": "http://example.com",
"api": "http://example.com",
"llms_txt": "http://example.com"
},
"contract_url": "http://example.com",
"output_schema": {},
"output": {},
"execution": {
"mode": "sync"
},
"callability": {
"state": "complete",
"missing": [
"string"
]
},
"compatibility": {},
"source": {
"catalog": "curated_marketplace",
"snapshot_version": 0,
"captured_at": "2019-08-24T14:15:22Z"
},
"operator_type": "first_party",
"operated_by_id": "string",
"settled_via_facilitator_id": "string",
"settlements": 0,
"last_verified_at": "2019-08-24T14:15:22Z",
"latency_p50_ms": 0
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| endpoint_id | string | false | none | none |
| url | string¦null | false | none | none |
| resource_type | string¦null | false | none | none |
| primary_protocol | string¦null | false | none | none |
| call | SearchEndpointCall | false | none | The machine-readable call contract for this endpoint: the verb, the provider-declared argument schema, and the worked examples. Always present; an endpoint whose provider declares nothing carries an empty method and null members rather than an absent block, so a caller canalways read input_schema without a presence check. Combine with thehit's url to construct the request. |
| price | SearchEndpointPrice | false | none | Price + provenance + freshness for this (endpoint × capability) pairing. Always present; an unpriced endpoint carries null amounts rather than an absent block, so a caller never has to distinguish "free" from "we do not know". |
| payment | [SearchPaymentOffer] | false | none | The settlement routes this endpoint's own 402 challenge published — one entry per rail × network × asset × payee it accepts. Sibling of call: that block says how to shape the request, this says how topay for it, so a caller can settle with its OWN x402/mpp SDK instead of guessing. A list because rails are irreducibly plural. Order is the provider's own preference order. Honest-empty when the pipeline observed no challenge. |
| access_methods | [SearchAccessMethod] | false | none | none |
| service | SearchCuratedService | false | none | none |
| operation | SearchCuratedOperation | false | none | none |
| docs | SearchHelperDocs | false | none | Free helper links for the upstream provider. GET these URLs. Do not send them through weft_fetch. |
| contract_url | string(uri) | false | none | Content-addressed Weft contract for the complete operation, alternatives, evidence, and known gaps. Authenticated GET. |
| output_schema | object¦null | false | none | none |
| output | object¦null | false | none | none |
| execution | SearchCuratedExecution | false | none | none |
| callability | SearchCuratedCallability | false | none | none |
| compatibility | object¦null | false | none | none |
| source | SearchCuratedSource | false | none | none |
| operator_type | string¦null | false | none | Who you are actually paying. first_party = operated by the providerthat makes the capability; reseller = resold, so the price carriessomeone else's margin; gateway = an intermediary routing the provider'scapability. Null until the platform resolves the operator. |
| operatedbyid | string¦null | false | none | none |
| settledviafacilitator_id | string¦null | false | none | none |
| settlements | integer¦null | false | none | Count of payments observed settling against this endpoint by ANYONE (chain-indexed), not just by Weft — the reliability signal a caller can act on. Null when unknown. |
| lastverifiedat | string(date-time)¦null | false | none | When Weft last CONFIRMED this endpoint answers — the most recent conclusive probe. Null when never probed, or when the latest probe errored: an endpoint we last failed to reach has no current verification. |
| latencyp50ms | integer¦null | false | none | Median time-to-first-byte in ms across the endpoint's probe call set. First-byte latency, not full-response time. Null when unmeasured (never 0). |
Enumerated Values
| Property | Value |
|---|---|
| operator_type | first_party |
| operator_type | reseller |
| operator_type | gateway |
SearchEndpointCall
{
"method": "string",
"input_schema": {},
"example_request": {},
"example_response": null
}
The machine-readable call contract for this endpoint: the verb, the
provider-declared argument schema, and the worked examples. Always
present; an endpoint whose provider declares nothing carries an empty
method and null members rather than an absent block, so a caller can
always read input_schema without a presence check. Combine with the
hit's url to construct the request.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| method | string | false | none | The HTTP verb to send. Empty string when neither the index nor the provider's own 402 challenge / OpenAPI spec declares one. |
| input_schema | object,boolean,null | false | none | The request JSON Schema, including boolean true/false schemas. Reviewed versioned contracts preserve the provider schema and apply separately authored client scope constraints in this projection. Null when no body schema is declared; query/path/header/cookie bindings remain in the full contract. Retrieve contract_url before constructing requests for versioned contracts. |
| example_request | object¦null | false | none | A worked set of arguments grouped by slot (query / body / path).Every value is one the PROVIDER published; never synthesized. |
| example_response | any | false | none | The provider's own worked response body — what you get for your money, before paying. Carried verbatim, never synthesized. |
SearchEndpointPrice
{
"indexed_usd": "string",
"atomic": 0,
"source": "probe",
"last_observed_at": "2019-08-24T14:15:22Z",
"live_verified": true
}
Price + provenance + freshness for this (endpoint × capability) pairing. Always present; an unpriced endpoint carries null amounts rather than an absent block, so a caller never has to distinguish "free" from "we do not know".
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| indexed_usd | string¦null | false | none | The indexed price in USD as a decimal string (e.g. "0.008") — the dollar value people and agents reason in. A decimal string, never a float; trailing zeros trimmed. Null when unpriced. |
| atomic | integer¦null | false | none | The same price in integer micro-USD — the settlement grain, exact. Null exactly when indexed_usd is. Use this for settlement. |
| source | string¦null | false | none | Where the amount came from. probe = observed live in the endpoint'sown 402 payment-required challenge — an observation, the strongest evidence we hold. inferred = derived from a spec (OpenAPI) or aresale edge by the extraction pipeline, so it can lag a provider's re-pricing. Null when the endpoint carries no price. |
| lastobservedat | string(date-time)¦null | false | none | When the amount was observed. Null when unrecorded. |
| live_verified | boolean | false | none | Whether this amount was confirmed against a live 402 challenge for THIS response. Search does not issue a payment-required probe at query time, so this is false today — treat an unflagged price asindexed, not verified. |
Enumerated Values
| Property | Value |
|---|---|
| source | probe |
| source | inferred |
SearchPaymentOffer
{
"protocol": "string",
"scheme": "string",
"network": "string",
"asset": "string",
"amount": "string",
"pay_to": "string",
"max_timeout_seconds": 0,
"facilitator": "string"
}
One settlement route the endpoint's own 402 challenge published — a (rail × network × asset × payee) tuple a caller can settle against directly with its own SDK.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| protocol | string | true | none | The payment rail (e.g. x402, mpp). |
| scheme | string¦null | false | none | none |
| network | string¦null | false | none | none |
| asset | string¦null | false | none | none |
| amount | string¦null | false | none | none |
| pay_to | string¦null | false | none | none |
| maxtimeoutseconds | integer¦null | false | none | none |
| facilitator | string¦null | false | none | none |
SearchAccessMethod
{
"access_method_id": "string",
"protocol": "x402",
"scheme": "string",
"network": "string",
"asset": "string",
"asset_decimals": 0,
"price": {},
"merchant": {},
"terms": {},
"status": "listed",
"weft_fetch": {
"state": "supported",
"reason": "string",
"contract_version": 0,
"coverage": "terminal_response"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| accessmethodid | string | true | none | none |
| protocol | string | true | none | none |
| scheme | string¦null | false | none | none |
| network | string¦null | false | none | none |
| asset | string¦null | false | none | none |
| asset_decimals | integer¦null | false | none | none |
| price | object | true | none | none |
| merchant | object | false | none | none |
| terms | object | false | none | none |
| status | string | false | none | none |
| weft_fetch | SearchWeftFetchCompatibility | true | none | none |
Enumerated Values
| Property | Value |
|---|---|
| protocol | x402 |
| protocol | mpp |
| protocol | free |
| status | listed |
| status | observed |
| status | unavailable |
SearchWeftFetchCompatibility
{
"state": "supported",
"reason": "string",
"contract_version": 0,
"coverage": "terminal_response"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| state | string | true | none | none |
| reason | string | true | none | none |
| contract_version | integer | true | none | none |
| coverage | string | false | none | Extent of the operation covered by weftfetch. `terminalresponse<br>covers the terminal result;submission_onlycovers submission but<br>not completion or retrieval of the result;none` means no coverage.When absent, coverage is unknown, not terminal. Do not infer terminal coverage from synchronous execution mode alone. |
Enumerated Values
| Property | Value |
|---|---|
| state | supported |
| state | unsupported |
| state | unknown |
| coverage | terminal_response |
| coverage | submission_only |
| coverage | none |
SearchHelperDocs
{
"website": "http://example.com",
"homepage": "http://example.com",
"api": "http://example.com",
"llms_txt": "http://example.com"
}
Free helper links for the upstream provider. GET these URLs.
Do not send them through weft_fetch.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| website | string(uri) | false | none | none |
| homepage | string(uri) | false | none | none |
| api | string(uri) | false | none | none |
| llms_txt | string(uri) | false | none | none |
SearchCuratedService
{
"id": "string",
"name": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | string | true | none | none |
| name | string | true | none | none |
SearchCuratedOperation
{
"id": "string",
"name": "string",
"description": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | string | true | none | none |
| name | string | true | none | none |
| description | string¦null | false | none | none |
SearchCuratedExecution
{
"mode": "sync"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| mode | string¦null | false | none | none |
Enumerated Values
| Property | Value |
|---|---|
| mode | sync |
| mode | async |
| mode | stream |
SearchCuratedCallability
{
"state": "complete",
"missing": [
"string"
]
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| state | string | true | none | none |
| missing | [string] | true | none | none |
Enumerated Values
| Property | Value |
|---|---|
| state | complete |
| state | incomplete |
SearchCuratedSource
{
"catalog": "curated_marketplace",
"snapshot_version": 0,
"captured_at": "2019-08-24T14:15:22Z"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| catalog | string | true | none | none |
| snapshot_version | integer | true | none | none |
| captured_at | string(date-time) | true | none | none |
Enumerated Values
| Property | Value |
|---|---|
| catalog | curated_marketplace |
SearchErrorResponse
{
"error": "INVALID_QUERY",
"details": {}
}
Bespoke error envelope returned by /api/v1/search for 422, 502,
and 500 responses. It differs from the standard ErrorResponse
envelope (no nested code/message/request_id); SDK clients
should handle both shapes for this endpoint until the controller
is migrated to the standard envelope.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| error | string | true | none | Stable error code. |
| details | object | false | none | Optional context. Shape varies by error code. |
Enumerated Values
| Property | Value |
|---|---|
| error | INVALID_QUERY |
| error | INVALIDMAXRESULTS |
| error | INVALID_FILTERS |
| error | INVALID_VIEW |
| error | UNKNOWN_PARAMETER |
| error | SEARCHUPSTREAMERROR |
| error | SEARCHBACKENDMISCONFIGURED |
FetchRequest
{
"url": "https://x402.api.agentmail.to/v0/inboxes",
"max_cost_usd": "0.05",
"allow_tempo_refill": false,
"max_total_cost_usd": "0.05",
"method": "GET",
"body": "string",
"headers": {
"Accept": "application/json",
"User-Agent": "my-agent/1.0"
},
"search_id": "11111111-1111-4111-8111-111111111111",
"operation_id": "string",
"access_method_id": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| url | string(uri) | true | none | Target URL. Must pass Weft's URL safety check (no SSRF / private IP ranges). |
| maxcostusd | string | false | none | Merchant-principal ceiling, excluding gas, provider fees and prerequisites. Defaults to 0.10 USD; use max_total_cost_usd for an all-in bound. |
| allowtemporefill | boolean | false | none | Whether this request may create, adopt, or enqueue a Base-to-Tempo refill. Only JSON booleans are accepted. Omission allows legacy refill behavior unless max_total_cost_usd is supplied, in whichcase refill is disabled. False leaves unrelated bridges and jobs unchanged and reports an unfunded Tempo pocket as INSUFFICIENT_BALANCE. This does not select a rail or bound fees. |
| maxtotalcost_usd | string | false | none | Optional all-in buyer-debit ceiling in USD, including principal, gas, provider fees and prerequisite operations. Requires a binding upstream upper bound before any payment effect. Current integrations have no such guarantee, so requests requiring wallet signing or payment fail closed with TOTAL_COST_UNVERIFIABLE, includingrecovery and replay of previous payments. No positive paid route is currently admitted in this mode. Estimates, expected sponsorship and receipts are not authority. Implies no refill; explicit allow_tempo_refill: true is invalid. Omission preserves legacy behavior. |
| method | string | false | none | HTTP method to use against the upstream. |
| body | any | false | none | Request body forwarded to the upstream. Pass null for GET. |
oneOf
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | string | false | none | none |
xor
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | object | false | none | none |
xor
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | null | false | none | none |
continued
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| headers | object | false | none | Headers forwarded to the upstream. Up to 32 headers, 4 KB total. The following are silently stripped: host,authorization, cookie, proxy-authorization,x-forwarded-*, x-real-ip, x-payment, connection,upgrade. |
| » additionalProperties | string | false | none | none |
| search_id | string(uuid) | false | none | The query_trace_id from the POST /api/v1/search response thatsurfaced this URL. Optional and advisory: it attributes the purchase to the search that found it, and is used only for measurement. It never affects payment, authorization, idempotency, or the response body — the buyer is always resolved from the credential, never from this field. A value that is not a well-formed handle is ignored rather than rejected, so an analytics mistake can never cost a fetch. |
| operation_id | string | false | none | Advisory operation id returned by search. |
| accessmethodid | string | false | none | Advisory access-method id returned by search; does not enforce a payment rail. |
Enumerated Values
| Property | Value |
|---|---|
| method | GET |
| method | POST |
| method | PUT |
| method | PATCH |
| method | DELETE |
| method | HEAD |
| method | OPTIONS |
FetchResponse
{
"status": 200,
"headers": {
"property1": "string",
"property2": "string"
},
"body_base64": "string",
"paid_usd": "0.002",
"held_usd": "0.002",
"payment_status": "pending",
"tx_hash": "string",
"protocol": "x402",
"artifact_id": 0,
"purchase_id": "pur_4V3n9mQ0b7XyK2sLpR8tWc1d"
}
Successful fetch envelope. body_base64 is the upstream artifact
bytes, base64-encoded. paid_usd, held_usd, payment_status,
tx_hash, and protocol describe the payment and settlement state.
purchase_id names the payment for a later status read.
To retrieve a SIWX-protected result, use HTTPS GET with
max_cost_usd: "0". Weft signs a fresh, resource-bound challenge with
the same buyer wallet. Retrieval never falls back to payment and
returns not_required, zero paid amount, and null held amount and
transaction hash. Polls are fresh, including with repeated idempotency
keys. A rejected or unsafe challenge returns SIWX_RETRIEVAL_FAILED.
Providers must support smart-wallet signatures. Auth-only challenges
also use this path; positive-budget purchases retain payment behavior.
paid_usd is "0.00" (never the nominal charge amount) until the
charge is CONFIRMED settled on-chain — a signed-but-unsettled hold
reports its amount in held_usd instead. This is a deliberate
honesty fix: earlier versions of this endpoint returned the nominal
amount in paid_usd unconditionally, even when the charge never
settled.
Money string format. Every USD amount on this surface is exact to the micro-dollar and never narrower than two decimals: a whole-cent amount renders "0.50", a sub-cent amount keeps its real precision ("0.000892"), and zero renders "0.00". Amounts are never rounded — an agent reconciling its own spend reads the truth, not a display value. Parse these as decimals; do NOT compare them as strings against a bare zero literal.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| status | integer | true | none | HTTP status returned by the upstream after the payment or SIWX authentication retry. |
| headers | object | true | none | Response headers from the upstream. |
| » additionalProperties | string | false | none | none |
| body_base64 | string | true | none | Base64-encoded response body. Empty string for empty bodies. |
| paid_usd | string,null¦null | true | none | USD amount actually settled on-chain. "0.00" for any charge that hasn't (yet, or ever) settled — a signed hold is not yet spend. See held_usd for the nominal amount in that case. Exact to themicro-dollar, minimum two decimals; parse as a decimal rather than string-comparing against a bare zero literal. |
| held_usd | string,null¦null | true | none | The nominal charge amount when paid_usd is "0.00" — a holdawaiting settlement, or a charge that failed/expired without ever settling. null once paid_usd reflects the real settlement.Same format as paid_usd: exact to the micro-dollar, minimum twodecimals. |
| payment_status | string | true | none | Agent-facing settlement status. not_required means SIWX walletauthentication returned the response without payment. pending = signed, no refusalsignal yet (settlement may still land, e.g. x402's async facilitator webhook). declined-pending = the merchant refusedbut the authorization isn't provably dead yet. declined /expired / reverted are terminal — the money never moved (or,for reverted, moved and then reversed on-chain) and never willfor this charge. |
| tx_hash | string,null¦null | true | none | Settlement transaction hash. Null until a settlement hash has been reported. |
| protocol | string | true | none | Payment protocol selected for this fetch. |
| artifact_id | integer,null¦null | true | none | Internal artifact identifier if the response was persisted; null otherwise. |
| purchase_id | string,null¦null | false | none | The buyer purchase that paid for this fetch: an opaque id thatGET /api/v1/purchases/{id} accepts and that purchase rows carryas purchase_id. It is not the sequential Purchase.id; do notparse or order by it. That purchase reports its own status, not payment_status: signed while the payment ispending, then settled (charged its amount_usd, which can beless than held_usd) or declined, locked, expired, orreverted (not charged). A settled purchase withfailure_reason: merchant_delivery_failed was charged, but nosuccessful paid response was delivered. An idempotent replay returns the same id, so bill at most once for each id. null when no payment was made (payment_status: not_required).The lookup needs purchase-history access: a wk_* key, a claimedwbt_* bearer, or a REST OAuth token with the balance scope. AnOAuth token issued for the MCP server cannot call this endpoint. Current servers always send this field. It is optional in this schema so that a client also accepts a response from an older server. |
Enumerated Values
| Property | Value |
|---|---|
| payment_status | settled |
| payment_status | pending |
| payment_status | declined-pending |
| payment_status | declined |
| payment_status | expired |
| payment_status | reverted |
| payment_status | not_required |
| protocol | x402 |
| protocol | mpp |
FetchErrorResponse
{
"error": "EXCEEDED_MAX_COST",
"details": {},
"policy": {
"max_tx_usd": "1.00",
"daily_limit_usd": "10.00",
"weekly_limit_usd": "50.00"
},
"balance": {
"promo_usd": "0.00",
"wallet_usdc": "12.34",
"total_usd": "12.34",
"spent_today_usd": "0.42",
"policy_used_today_usd": "0.72"
},
"dashboard_url": "http://example.com"
}
Bespoke error envelope for /api/v1/fetch. Every error carries the
buyer's current policy, balance, and a dashboard_url so a CLI
can render an actionable message without a second round-trip.
error values include the fixed codes listed below plus the
POLICY_VIOLATION_<REASON> family, where <REASON> is the violated
policy field (MAX_TX, DAILY, or WEEKLY — see
PolicyViolation::REASONS).
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| error | string | true | none | Stable error code. |
| details | object | true | none | Optional context. Shape varies by error code.purchase_id (opaque string) is present when the failure follows apayment row for this request — a new, resumed, or replayed payment. It is the id that GET /api/v1/purchases/{id} accepts: read thatpurchase to learn whether the charge settled, instead of assuming either outcome. It is absent when the request was refused before any payment row existed. |
| policy | SpendingPolicy | true | none | none |
| balance | FetchBalanceSnapshot | true | none | Compact balance snapshot returned inside FetchErrorResponse. Lessrich than BalanceResponse — just the fields a CLI needs to explainwhy a fetch failed. |
| dashboard_url | string(uri) | true | none | Deep-link to the dashboard's policy page. |
Enumerated Values
| Property | Value |
|---|---|
| error | INVALID_REQUEST |
| error | UNKNOWN_PARAMETER |
| error | INVALID_URL |
| error | INVALIDMAXCOST_USD |
| error | INVALIDALLOWTEMPO_REFILL |
| error | INVALIDMAXTOTALCOSTUSD |
| error | INCOMPATIBLEFETCHCONTROLS |
| error | UNSUPPORTED_METHOD |
| error | INVALID_BODY |
| error | INVALID_HEADERS |
| error | INVALIDIDEMPOTENCYKEY |
| error | IDEMPOTENCY_CONFLICT |
| error | ACCOUNT_CLOSING |
| error | EXCEEDEDMAXCOST |
| error | TOTALCOSTUNVERIFIABLE |
| error | FUNDING_PENDING |
| error | INSUFFICIENT_BALANCE |
| error | PAYMENTAUTHORIZATIONREQUIRED |
| error | WALLETSETUPINCOMPLETE |
| error | MERCHANTRETURNEDNON_402 |
| error | MERCHANT_TIMEOUT |
| error | MERCHANTCONNECTIONFAILED |
| error | PAIDDELIVERYFAILED |
| error | DELIVERYREPLAYUNAVAILABLE |
| error | ARTIFACTTOOLARGE |
| error | DENYLISTED_RECIPIENT |
| error | WALLETENVIRONMENTMISMATCH |
| error | UNSUPPORTED_ASSET |
| error | UNSUPPORTEDPAYMENTMETHOD |
| error | SETTLEMENT_FAILED |
| error | SETTLEMENT_PENDING |
| error | POLICYVIOLATIONMAX_TX |
| error | POLICYVIOLATIONDAILY |
| error | POLICYVIOLATIONWEEKLY |
FetchBalanceSnapshot
{
"promo_usd": "0.00",
"wallet_usdc": "12.34",
"total_usd": "12.34",
"spent_today_usd": "0.42",
"policy_used_today_usd": "0.72"
}
Compact balance snapshot returned inside FetchErrorResponse. Less
rich than BalanceResponse — just the fields a CLI needs to explain
why a fetch failed.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| promo_usd | string | true | none | none |
| wallet_usdc | string¦null | true | none | Live Base USDC balance read server-side through Crossmint. Null when Crossmint is unavailable; never treat null as zero. |
| total_usd | string¦null | true | none | Aggregated USD balance across Base USDC and Tempo dollar tokens, exact to the micro-dollar. Null when either pocket is unreachable. |
| spenttodayusd | string | true | none | Settled USD spend in the current calendar day (UTC). |
| policyusedtoday_usd | string | true | none | USD counted against today's policy limit, including pending authorizations and settled spend. |