Weft API Reference
Agent onboarding flows, optimized for automated clients.
Quickstart
- Create an account and confirm email
- Sign in to get a session token
- Create an API key via the dashboard or
POST /api/v1/api_keys - Use your API key as a Bearer token to access the API
- List your payments, agents, and manage API keys
Base URLs
https://weft.networkhttps://staging.weft.networkhttp://localhost:3000
Authentication
Auth endpoints (sign up, sign in, etc.) do not require authentication.
All other endpoints require a Bearer token via Authorization: Bearer <token>. Use an API key (ax_live_*) obtained from the dashboard or the Create API Key endpoint.
SDKs
Install the SDK for your language:
# TypeScript / JavaScript
npm install @weft-labs/sdk
# Python
pip install weft-sdk
# Ruby
gem install weft-sdk
# Go
go get github.com/weft-labs/weft-sdk/go
TypeScript
import { Configuration, AuthApi, PaymentsApi, AgentsApi, APIKeysApi, AccountApi } from "@weft-labs/sdk";
// Auth endpoints (no token needed)
const auth = new AuthApi();
// Authenticated endpoints (API key required)
const config = new Configuration({
accessToken: "ax_live_your_api_key_here",
});
const payments = new PaymentsApi(config);
const agents = new AgentsApi(config);
const apiKeys = new APIKeysApi(config);
const account = new AccountApi(config);
Python
import weft_sdk.generated
from weft_sdk.generated.api import payments_api, agents_api
configuration = weft_sdk.generated.Configuration(
access_token = "ax_live_your_api_key_here"
)
with weft_sdk.generated.ApiClient(configuration) as api_client:
payments = payments_api.PaymentsApi(api_client)
agents = agents_api.AgentsApi(api_client)
Ruby
require 'weft-sdk'
Weft.configure do |config|
config.access_token = 'ax_live_your_api_key_here'
end
payments = Weft::PaymentsApi.new
agents = Weft::AgentsApi.new
Go
import openapiclient "github.com/weft-labs/weft-sdk/go/generated"
configuration := openapiclient.NewConfiguration()
apiClient := openapiclient.NewAPIClient(configuration)
SDK examples are shown alongside each endpoint below. Use the language tabs to switch between implementations.
The Weft API is the buyer-runtime surface that powers the weft CLI,
the hosted MCP server (weft.network/mcp), and any third-party agent
that wants to discover and pay for paid resources on Weft. v1 covers
five buyer concerns:
- Account onboarding (
/api/v1/auth/*,/api/v1/me) - CLI authentication (
/api/v1/api_keys) - Wallet visibility (
/api/v1/balance) - Discovery (
/api/v1/search) - Paid execution (
/api/v1/fetch) - Purchase history (
/api/v1/payments)
Seller-side concerns (agent management, payout analytics, webhook
delivery, the public storefront for data_api resources) live in the
dashboard and are intentionally not documented here. They will be
split out into a separate, dashboard-scoped spec when they need to be
SDK-consumable.
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.
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 |
No authentication required
SDK
AuthResponse signUp(signUpRequest)
import {
Configuration,
AuthApi,
} from '@weft-labs/sdk';
import type { SignUpOperationRequest } from '@weft-labs/sdk';
const api = new AuthApi();
const body = {
// SignUpRequest
signUpRequest: ...,
} satisfies SignUpOperationRequest;
const data = await api.signUp(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| signUpRequest | SignUpRequest |
Returns: AuthResponse
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
SDK
AuthResponse confirmAccount(confirmRequest)
import {
Configuration,
AuthApi,
} from '@weft-labs/sdk';
import type { ConfirmAccountRequest } from '@weft-labs/sdk';
const api = new AuthApi();
const body = {
// ConfirmRequest
confirmRequest: ...,
} satisfies ConfirmAccountRequest;
const data = await api.confirmAccount(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| confirmRequest | ConfirmRequest |
Returns: AuthResponse
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
SDK
MessageResponse resendConfirmation(resendConfirmationRequest)
import {
Configuration,
AuthApi,
} from '@weft-labs/sdk';
import type { ResendConfirmationOperationRequest } from '@weft-labs/sdk';
const api = new AuthApi();
const body = {
// ResendConfirmationRequest
resendConfirmationRequest: ...,
} satisfies ResendConfirmationOperationRequest;
const data = await api.resendConfirmation(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| resendConfirmationRequest | ResendConfirmationRequest |
Returns: MessageResponse
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
SDK
AuthResponse signIn(signInRequest)
import {
Configuration,
AuthApi,
} from '@weft-labs/sdk';
import type { SignInOperationRequest } from '@weft-labs/sdk';
const api = new AuthApi();
const body = {
// SignInRequest
signInRequest: ...,
} satisfies SignInOperationRequest;
const data = await api.signIn(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| signInRequest | SignInRequest |
Returns: AuthResponse
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
SDK
MessageResponse requestPasswordReset(passwordResetRequest)
import {
Configuration,
AuthApi,
} from '@weft-labs/sdk';
import type { RequestPasswordResetRequest } from '@weft-labs/sdk';
const api = new AuthApi();
const body = {
// PasswordResetRequest
passwordResetRequest: ...,
} satisfies RequestPasswordResetRequest;
const data = await api.requestPasswordReset(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| passwordResetRequest | PasswordResetRequest |
Returns: MessageResponse
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
SDK
MessageResponse updatePassword(passwordUpdateRequest)
import {
Configuration,
AuthApi,
} from '@weft-labs/sdk';
import type { UpdatePasswordRequest } from '@weft-labs/sdk';
const api = new AuthApi();
const body = {
// PasswordUpdateRequest
passwordUpdateRequest: ...,
} satisfies UpdatePasswordRequest;
const data = await api.updatePassword(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| passwordUpdateRequest | PasswordUpdateRequest |
Returns: MessageResponse
Account
Current user account information.
Get current account
GET /api/v1/me
200 Response
{
"data": {
"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 user account | MeResponse |
| 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
SDK
MeResponse getMe()
import {
Configuration,
AccountApi,
} from '@weft-labs/sdk';
import type { GetMeRequest } from '@weft-labs/sdk';
const config = new Configuration({
// Configure HTTP bearer authorization: bearerAuth
accessToken: "YOUR BEARER TOKEN",
});
const api = new AccountApi(config);
const data = await api.getMe();
console.log(data);
Parameters: This endpoint does not need any parameter.
Returns: MeResponse
API Keys
Manage API keys used by the CLI and any direct HTTP caller.
List API keys
GET /api/v1/api_keys
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
SDK
ApiKeyListResponse listApiKeys()
import {
Configuration,
APIKeysApi,
} from '@weft-labs/sdk';
import type { ListApiKeysRequest } from '@weft-labs/sdk';
const config = new Configuration({
// Configure HTTP bearer authorization: bearerAuth
accessToken: "YOUR BEARER TOKEN",
});
const api = new APIKeysApi(config);
const data = await api.listApiKeys();
console.log(data);
Parameters: This endpoint does not need any parameter.
Returns: ApiKeyListResponse
Create an API key
POST /api/v1/api_keys
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
SDK
ApiKeyCreatedResponse createApiKey(createApiKeyRequest)
import {
Configuration,
APIKeysApi,
} from '@weft-labs/sdk';
import type { CreateApiKeyOperationRequest } from '@weft-labs/sdk';
const config = new Configuration({
// Configure HTTP bearer authorization: bearerAuth
accessToken: "YOUR BEARER TOKEN",
});
const api = new APIKeysApi(config);
const body = {
// CreateApiKeyRequest
createApiKeyRequest: ...,
} satisfies CreateApiKeyOperationRequest;
const data = await api.createApiKey(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| createApiKeyRequest | CreateApiKeyRequest |
Returns: ApiKeyCreatedResponse
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
SDK
MessageResponse revokeApiKey(id)
import {
Configuration,
APIKeysApi,
} from '@weft-labs/sdk';
import type { RevokeApiKeyRequest } from '@weft-labs/sdk';
const config = new Configuration({
// Configure HTTP bearer authorization: bearerAuth
accessToken: "YOUR BEARER TOKEN",
});
const api = new APIKeysApi(config);
const body = {
// number | API key ID
id: 56,
} satisfies RevokeApiKeyRequest;
const data = await api.revokeApiKey(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| id | number | API key ID | [Defaults to undefined] |
Returns: MessageResponse
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. wallet.balance_usdc is fetched live from Privy;
if Privy is unreachable the field returns "0.00" rather than
erroring the whole call.
Account-scoped: the bearer must be a buyer-scoped API key.
200 Response
{
"promo": {
"balance_usd": "0.00",
"scope": "weft-search",
"expires_at": "2019-08-24T14:15:22Z"
},
"wallet": {
"address": "string",
"balance_usdc": "12.34",
"tempo_usd": "3.00",
"total_usd": "15.34",
"network": "string"
},
"spent_today_usd": "0.42",
"spent_week_usd": "3.10",
"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
SDK
BalanceResponse getBalance()
import {
Configuration,
BalanceApi,
} from '@weft-labs/sdk';
import type { GetBalanceRequest } from '@weft-labs/sdk';
const config = new Configuration({
// Configure HTTP bearer authorization: bearerAuth
accessToken: "YOUR BEARER TOKEN",
});
const api = new BalanceApi(config);
const data = await api.getBalance();
console.log(data);
Parameters: This endpoint does not need any parameter.
Returns: BalanceResponse
Search
Semantic search across the Weft index of paid agent resources.
Returns A2A-shaped AgentCards with pricing, skills, ranking, and
endpoints. The mock backend (default while the search-platform is
unshipped) reads YAML fixtures from db/seeds/mock-search/; the
platform backend proxies to the upstream search service. Both share
the same response envelope.
Search the Weft index
POST /api/v1/search
Semantic search over the Weft index of paid agent resources. The
request body carries a free-text query, an optional limit, and
an optional filters object that narrows by price band, payment
protocol, agent protocol, or domain tag. Filters are applied as a
post-filter after the embedding score is computed.
Backend selection is server-side via the SEARCH_BACKEND env var:
mock (default, reads YAML fixtures and sets _mock: true in the
response) or platform (proxies to the upstream search service).
Both backends return the same envelope.
Account-scoped: the bearer token must be a buyer-scoped API key. Free for authenticated buyers in v1; billing is planned for a later release.
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.
Request body
{
"query": "send email from an agent",
"limit": 10,
"filters": {
"min_price_usd": "0.01",
"max_price_usd": "0.10",
"payment_protocol": "x402",
"agent_protocol": "a2a",
"domain": "string"
}
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | SearchRequest | true | none |
200 Response
{
"results": [
{
"id": "string",
"score": 1,
"protocol": "a2a",
"domain": [
"string"
],
"reseller": "string",
"upstream": "string",
"agent_card": {
"name": "string",
"description": "string",
"url": "http://example.com",
"version": "string",
"protocolVersion": "string",
"capabilities": {},
"defaultInputModes": [
"string"
],
"defaultOutputModes": [
"string"
],
"skills": [
{
"id": "string",
"name": "string",
"description": "string",
"tags": [
"string"
],
"examples": [
"string"
],
"inputModes": [
"string"
],
"outputModes": [
"string"
],
"streaming": true,
"endpoint": {
"method": "string",
"path": "string"
},
"price_usd": "string"
}
]
},
"pricing": {
"protocol": "x402",
"scheme": "per_call",
"amount_usd": "string",
"network": "string"
},
"ranking": {
"popularity_score": 1,
"settlement_count_30d": 0,
"success_rate_30d": 1,
"p50_latency_ms": 0,
"first_seen_at": "2019-08-24T14:15:22Z"
},
"endpoints": {
"a2a": "http://example.com",
"mcp": "http://example.com",
"openapi": "http://example.com",
"weft_fetch_target": "http://example.com"
}
}
],
"paid_usd": "string",
"tx_hash": "string",
"artifact_id": "string",
"_mock": true
}
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|
|422|Unprocessable Entity|Invalid query (empty/missing)|SearchErrorResponse|
|500|Internal Server Error|Backend misconfigured (SEARCH_BACKEND unset or unknown)|SearchErrorResponse|
|502|Bad Gateway|Upstream search backend error (platform backend only)|SearchErrorResponse|
Requires authentication: Bearer token
SDK
SearchResponse search(searchRequest)
import {
Configuration,
SearchApi,
} from '@weft-labs/sdk';
import type { SearchOperationRequest } from '@weft-labs/sdk';
const config = new Configuration({
// Configure HTTP bearer authorization: bearerAuth
accessToken: "YOUR BEARER TOKEN",
});
const api = new SearchApi(config);
const body = {
// SearchRequest
searchRequest: ...,
} satisfies SearchOperationRequest;
const data = await api.search(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| searchRequest | SearchRequest |
Returns: SearchResponse
Fetch
The universal x402 fetch proxy. POST any URL the agent wants to
reach, with a max_cost_usd ceiling; Weft handles the 402 Payment
Required challenge, signs the EIP-3009 payment from the buyer's
wallet, 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 proxy)
POST /api/v1/fetch
Universal x402 fetch proxy. The caller provides a target url,
a hard max_cost_usd ceiling, and optional method / body /
headers. Weft:
- Issues the request.
- On
402 Payment Required, parses the merchant's challenge. - Compares the asking price to
max_cost_usdand the buyer's policy (max_tx_usd, daily/weekly limits). - Signs an EIP-3009 transfer from the buyer's wallet.
- Replays the request with the
X-Paymentheader. - Streams the upstream artifact back, base64-encoded under
body_base64, withpaid_usd,tx_hash, and the merchant's reputation snapshot.
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.
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",
"method": "GET",
"body": "string",
"headers": {
"Accept": "application/json",
"User-Agent": "my-agent/1.0"
}
}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| body | body | FetchRequest | true | none |
200 Response
{
"status": 200,
"headers": {
"property1": "string",
"property2": "string"
},
"body_base64": "string",
"paid_usd": "0.002",
"tx_hash": "string",
"artifact_id": 0,
"merchant": {
"address": "string",
"settlement_count": 0,
"first_seen_at": "2019-08-24T14:15:22Z",
"dispute_count": 0
}
}
Responses
| Status | Meaning | Description | Schema |
|---|---|---|---|
| 200 | OK | Fetch succeeded (or upstream was free); 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). Also
CONVERSION_UNAVAILABLE — a cross-chain provision route is down
while the buyer's funds are intact (details.venue,
details.retryable: true); retry later, do NOT top up. All use
FetchErrorResponse.|FetchErrorResponse|
|403|Forbidden|Policy violation, 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 three
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|
|413|Payload Too Large|Upstream artifact exceeded the proxy's size cap.|FetchErrorResponse|
|422|Unprocessable Entity|Invalid request URL, or the merchant advertised a payment method Weft cannot sign.|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.|FetchErrorResponse|
|502|Bad Gateway|Settlement signing failed on Weft's side (SETTLEMENT_FAILED).|FetchErrorResponse|
Response Schema
Enumerated Values
| Property | Value |
|---|---|
| error | INVALID_URL |
| error | EXCEEDEDMAXCOST |
| error | INSUFFICIENT_BALANCE |
| error | CONVERSION_UNAVAILABLE |
| error | MERCHANTRETURNEDNON_402 |
| error | ARTIFACTTOOLARGE |
| error | DENYLISTED_RECIPIENT |
| error | WALLETENVIRONMENTMISMATCH |
| error | SETTLEMENT_FAILED |
| error | UNSUPPORTEDPAYMENTMETHOD |
| error | POLICYVIOLATIONMAX_TX |
| error | POLICYVIOLATIONDAILY |
| error | POLICYVIOLATIONWEEKLY |
| error | insufficient_scope |
Requires authentication: Bearer token
SDK
FetchResponse fetch(fetchRequest)
import {
Configuration,
FetchApi,
} from '@weft-labs/sdk';
import type { FetchOperationRequest } from '@weft-labs/sdk';
const config = new Configuration({
// Configure HTTP bearer authorization: bearerAuth
accessToken: "YOUR BEARER TOKEN",
});
const api = new FetchApi(config);
const body = {
// FetchRequest
fetchRequest: ...,
} satisfies FetchOperationRequest;
const data = await api.fetch(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| fetchRequest | FetchRequest |
Returns: FetchResponse
Payments
Payment history and individual purchase details.
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",
"fee_amount": 0,
"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
SDK
PaymentListResponse listPayments(page, perPage)
import {
Configuration,
PaymentsApi,
} from '@weft-labs/sdk';
import type { ListPaymentsRequest } from '@weft-labs/sdk';
const config = new Configuration({
// Configure HTTP bearer authorization: bearerAuth
accessToken: "YOUR BEARER TOKEN",
});
const api = new PaymentsApi(config);
const body = {
// number | Page number (optional)
page: 56,
// number | Items per page (optional)
perPage: 56,
} satisfies ListPaymentsRequest;
const data = await api.listPayments(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| page | number | Page number | [Optional] [Defaults to 1] |
| perPage | number | Items per page | [Optional] [Defaults to 25] |
Returns: PaymentListResponse
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",
"fee_amount": 0,
"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
SDK
PaymentResponse getPayment(id)
import {
Configuration,
PaymentsApi,
} from '@weft-labs/sdk';
import type { GetPaymentRequest } from '@weft-labs/sdk';
const config = new Configuration({
// Configure HTTP bearer authorization: bearerAuth
accessToken: "YOUR BEARER TOKEN",
});
const api = new PaymentsApi(config);
const body = {
// number | Payment ID
id: 56,
} satisfies GetPaymentRequest;
const data = await api.getPayment(body);
console.log(data);
Parameters:
| Name | Type | Description | Notes |
|---|---|---|---|
| id | number | Payment ID | [Defaults to undefined] |
Returns: PaymentResponse
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
SDK
ResourceEnrollmentResponse enroll_resource(resource_enrollment_request)
import weft_sdk.generated
from weft_sdk.generated.models.resource_enrollment_request import ResourceEnrollmentRequest
from weft_sdk.generated.models.resource_enrollment_response import ResourceEnrollmentResponse
from weft_sdk.generated.rest import ApiException
configuration = weft_sdk.generated.Configuration(
)
# Enter a context with an instance of the API client
with weft_sdk.generated.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = weft_sdk.generated.ResourcesApi(api_client)
resource_enrollment_request = weft_sdk.generated.ResourceEnrollmentRequest() # ResourceEnrollmentRequest |
try:
# Self-enroll a ghost resource (public, no auth)
api_response = api_instance.enroll_resource(resource_enrollment_request)
print(api_response)
except Exception as e:
print("Exception when calling ResourcesApi->enroll_resource: %s\n" % e)
Schemas
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": {
"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 | AccountDetails | true | 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). |
AccountDetails
{
"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 |
|---|---|---|---|---|
| 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 |
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",
"fee_amount": 0,
"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 |
| fee_amount | integer | 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",
"fee_amount": 0,
"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",
"fee_amount": 0,
"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 |
BalanceResponse
{
"promo": {
"balance_usd": "0.00",
"scope": "weft-search",
"expires_at": "2019-08-24T14:15:22Z"
},
"wallet": {
"address": "string",
"balance_usdc": "12.34",
"tempo_usd": "3.00",
"total_usd": "15.34",
"network": "string"
},
"spent_today_usd": "0.42",
"spent_week_usd": "3.10",
"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), 2dp. |
| spentweekusd | string | true | none | USD spent in the current calendar week (UTC, Monday start), 2dp. |
| 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
{
"address": "string",
"balance_usdc": "12.34",
"tempo_usd": "3.00",
"total_usd": "15.34",
"network": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| address | string¦null | true | none | EVM address of the buyer's Privy-managed wallet. Null if no wallet provisioned. |
| balance_usdc | string | true | none | Live Base USDC balance, 2dp. Returns "0.00" if the upstream wallet provider is unreachable. |
| tempo_usd | string¦null | true | none | Aggregated USD value of the allowlisted Tempo TIP-20 dollar tokens on the wallet's paired Tempo chain, 2dp. null when the value is UNKNOWN — the Tempo RPC read failed, or no dollar token is allowlisted for that chain yet (e.g. Tempo mainnet pre-launch). A null here is never "0.00"; it means "we couldn't determine it", and total_usd then reflects the Base component only. |
| total_usd | string¦null | true | none | Single aggregated USD balance = Base USDC + Tempo dollar tokens, 2dp. When tempo_usd is null (unavailable/unallowlisted) this equals balance_usdc alone. Null when the Base USDC provider is unreachable, because the surface never claims zero for a component it could not read. |
| network | string¦null | true | none | Wallet network (e.g. base-sepolia). |
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",
"limit": 10,
"filters": {
"min_price_usd": "0.01",
"max_price_usd": "0.10",
"payment_protocol": "x402",
"agent_protocol": "a2a",
"domain": "string"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| query | string | true | none | Free-text query. Required and non-empty. |
| limit | integer | false | none | Max number of hits to return. Clamped to [1, 50]. |
| filters | SearchFilters | false | none | Optional narrowing applied after the embedding score is computed. Unknown keys are rejected. Price filters use the agent's cheapest skill for max_price_usd and the most-expensive skill formin_price_usd, so an agent stays in results as long as any oneof its skills satisfies the band. |
SearchFilters
{
"min_price_usd": "0.01",
"max_price_usd": "0.10",
"payment_protocol": "x402",
"agent_protocol": "a2a",
"domain": "string"
}
Optional narrowing applied after the embedding score is computed.
Unknown keys are rejected. Price filters use the agent's cheapest
skill for max_price_usd and the most-expensive skill for
min_price_usd, so an agent stays in results as long as any one
of its skills satisfies the band.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| minpriceusd | string | false | none | Decimal USD floor; agent must have at least one skill priced at or above this. |
| maxpriceusd | string | false | none | Decimal USD ceiling; agent must have at least one skill priced at or below this. |
| payment_protocol | string | false | none | Payment protocol the agent settles on. |
| agent_protocol | string | false | none | Agent protocol surface (Agent-to-Agent, MCP, raw OpenAPI, or AgentNet). |
| domain | string | false | none | Substring match against any of the agent's declared domain tags (e.g. email, sales, enrichment). |
Enumerated Values
| Property | Value |
|---|---|
| payment_protocol | x402 |
| payment_protocol | mpp |
| payment_protocol | free |
| agent_protocol | a2a |
| agent_protocol | mcp |
| agent_protocol | openapi |
| agent_protocol | AgentNet |
SearchResponse
{
"results": [
{
"id": "string",
"score": 1,
"protocol": "a2a",
"domain": [
"string"
],
"reseller": "string",
"upstream": "string",
"agent_card": {
"name": "string",
"description": "string",
"url": "http://example.com",
"version": "string",
"protocolVersion": "string",
"capabilities": {},
"defaultInputModes": [
"string"
],
"defaultOutputModes": [
"string"
],
"skills": [
{
"id": "string",
"name": "string",
"description": "string",
"tags": [
"string"
],
"examples": [
"string"
],
"inputModes": [
"string"
],
"outputModes": [
"string"
],
"streaming": true,
"endpoint": {
"method": "string",
"path": "string"
},
"price_usd": "string"
}
]
},
"pricing": {
"protocol": "x402",
"scheme": "per_call",
"amount_usd": "string",
"network": "string"
},
"ranking": {
"popularity_score": 1,
"settlement_count_30d": 0,
"success_rate_30d": 1,
"p50_latency_ms": 0,
"first_seen_at": "2019-08-24T14:15:22Z"
},
"endpoints": {
"a2a": "http://example.com",
"mcp": "http://example.com",
"openapi": "http://example.com",
"weft_fetch_target": "http://example.com"
}
}
],
"paid_usd": "string",
"tx_hash": "string",
"artifact_id": "string",
"_mock": true
}
Spec-11 search envelope. paid_usd, tx_hash, and artifact_id
are reserved for a later release that adds per-call billing and
artifact persistence; they are always null in v1. _mock: true
is set only by the mock backend.
Result rows: the mock backend (SEARCH_BACKEND=mock, the default
while the real index is unshipped) emits the rich, SDK-facing
SearchResult shape. The legacy platform backend proxies the
upstream search service and passes its result rows through verbatim —
Weft does not own or reshape that payload, so those rows are typed as
a free-form object. SDK clients on v1 should treat unknown row shapes
defensively until the platform backend is retrofitted to the
SearchResult contract (specs 07 + 10).
Because the PlatformSearchResult branch is intentionally permissive
(free-form, to admit the un-owned platform rows), the anyOf is
satisfied by any object — so the committee response-validation gate does
NOT strictly validate result-row shapes; the rich SearchResult
contract is instead guarded by the /api/v1/search request spec.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| results | [anyOf] | true | none | none |
anyOf
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | SearchResult | false | none | none |
or
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| » anonymous | PlatformSearchResult | false | none | A result row from the legacy platform search backend. The/api/v1/search controller proxies the upstream search service andpasses its rows through verbatim, so Weft does not own or constrain this shape. Documented as a free-form object purely so the canonical spec stays truthful about both backends. SDK clients should prefer the rich SearchResult shape and treat platform rows defensively. |
continued
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| paid_usd | string¦null | false | none | Always null in v1. |
| tx_hash | string¦null | false | none | Always null in v1. |
| artifact_id | string¦null | false | none | Always null in v1. |
| _mock | boolean | false | none | Present and true only when served by the mock backend. |
SearchResult
{
"id": "string",
"score": 1,
"protocol": "a2a",
"domain": [
"string"
],
"reseller": "string",
"upstream": "string",
"agent_card": {
"name": "string",
"description": "string",
"url": "http://example.com",
"version": "string",
"protocolVersion": "string",
"capabilities": {},
"defaultInputModes": [
"string"
],
"defaultOutputModes": [
"string"
],
"skills": [
{
"id": "string",
"name": "string",
"description": "string",
"tags": [
"string"
],
"examples": [
"string"
],
"inputModes": [
"string"
],
"outputModes": [
"string"
],
"streaming": true,
"endpoint": {
"method": "string",
"path": "string"
},
"price_usd": "string"
}
]
},
"pricing": {
"protocol": "x402",
"scheme": "per_call",
"amount_usd": "string",
"network": "string"
},
"ranking": {
"popularity_score": 1,
"settlement_count_30d": 0,
"success_rate_30d": 1,
"p50_latency_ms": 0,
"first_seen_at": "2019-08-24T14:15:22Z"
},
"endpoints": {
"a2a": "http://example.com",
"mcp": "http://example.com",
"openapi": "http://example.com",
"weft_fetch_target": "http://example.com"
}
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | string | true | none | Stable agent identifier (e.g. weft:agent:agentmail). |
| score | number(double) | true | none | Cosine similarity score, clipped to [0, 1]. |
| protocol | string | true | none | Agent protocol surface. |
| domain | [string] | true | none | Domain tags declared by the agent. |
| reseller | string¦null | false | none | Reseller slug if this agent is fronted by an aggregator (e.g. locus). |
| upstream | string¦null | false | none | Upstream provider hostname when fronted by a reseller. |
| agent_card | SearchAgentCard | true | none | A2A-spec-shaped AgentCard. Free-form capabilities and per-skillmetadata follow the upstream A2A schema; documented here only at the surface level Weft's index actually populates. |
| pricing | SearchPricing | true | none | Aggregate pricing at the agent level. recipient_address isintentionally absent from the index — under x402 it is delivered per-request in the 402 Payment Required challenge from the agent. |
| ranking | SearchRanking | true | none | none |
| endpoints | SearchEndpoints | true | none | Per-protocol entry points for the agent. Any field may be nullif the agent does not expose that protocol surface. |
Enumerated Values
| Property | Value |
|---|---|
| protocol | a2a |
| protocol | mcp |
| protocol | openapi |
| protocol | AgentNet |
PlatformSearchResult
{}
A result row from the legacy platform search backend. The
/api/v1/search controller proxies the upstream search service and
passes its rows through verbatim, so Weft does not own or constrain
this shape. Documented as a free-form object purely so the canonical
spec stays truthful about both backends. SDK clients should prefer the
rich SearchResult shape and treat platform rows defensively.
Properties
None
SearchAgentCard
{
"name": "string",
"description": "string",
"url": "http://example.com",
"version": "string",
"protocolVersion": "string",
"capabilities": {},
"defaultInputModes": [
"string"
],
"defaultOutputModes": [
"string"
],
"skills": [
{
"id": "string",
"name": "string",
"description": "string",
"tags": [
"string"
],
"examples": [
"string"
],
"inputModes": [
"string"
],
"outputModes": [
"string"
],
"streaming": true,
"endpoint": {
"method": "string",
"path": "string"
},
"price_usd": "string"
}
]
}
A2A-spec-shaped AgentCard. Free-form capabilities and per-skill
metadata follow the upstream A2A schema; documented here only at
the surface level Weft's index actually populates.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| name | string | false | none | none |
| description | string | false | none | none |
| url | string(uri) | false | none | none |
| version | string | false | none | none |
| protocolVersion | string | false | none | none |
| capabilities | object | false | none | none |
| defaultInputModes | [string] | false | none | none |
| defaultOutputModes | [string] | false | none | none |
| skills | [SearchSkill] | false | none | none |
SearchSkill
{
"id": "string",
"name": "string",
"description": "string",
"tags": [
"string"
],
"examples": [
"string"
],
"inputModes": [
"string"
],
"outputModes": [
"string"
],
"streaming": true,
"endpoint": {
"method": "string",
"path": "string"
},
"price_usd": "string"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| id | string | false | none | none |
| name | string | false | none | none |
| description | string | false | none | none |
| tags | [string] | false | none | none |
| examples | [string] | false | none | none |
| inputModes | [string] | false | none | none |
| outputModes | [string] | false | none | none |
| streaming | boolean | false | none | True for skills that require streaming transport (e.g. websockets). Streaming skills are filtered out of results by default so non-streaming clients only see callable skills. |
| endpoint | object | false | none | none |
| » method | string | false | none | HTTP method or WEBSOCKET for streaming skills. |
| » path | string | false | none | none |
| price_usd | string | false | none | Per-call price in USD for this individual skill. |
SearchPricing
{
"protocol": "x402",
"scheme": "per_call",
"amount_usd": "string",
"network": "string"
}
Aggregate pricing at the agent level. recipient_address is
intentionally absent from the index — under x402 it is delivered
per-request in the 402 Payment Required challenge from the agent.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| protocol | string | false | none | none |
| scheme | string | false | none | none |
| amount_usd | string | false | none | Default per-call amount; individual skills may override via price_usd. |
| network | string | false | none | Settlement network (e.g. base-sepolia). |
Enumerated Values
| Property | Value |
|---|---|
| protocol | x402 |
| protocol | mpp |
| protocol | free |
SearchRanking
{
"popularity_score": 1,
"settlement_count_30d": 0,
"success_rate_30d": 1,
"p50_latency_ms": 0,
"first_seen_at": "2019-08-24T14:15:22Z"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| popularity_score | number(double) | false | none | none |
| settlementcount30d | integer | false | none | none |
| successrate30d | number(double) | false | none | none |
| p50latencyms | integer | false | none | none |
| firstseenat | string(date-time) | false | none | none |
SearchEndpoints
{
"a2a": "http://example.com",
"mcp": "http://example.com",
"openapi": "http://example.com",
"weft_fetch_target": "http://example.com"
}
Per-protocol entry points for the agent. Any field may be null
if the agent does not expose that protocol surface.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| a2a | string(uri)¦null | false | none | none |
| mcp | string(uri)¦null | false | none | none |
| openapi | string(uri)¦null | false | none | URL to the agent's OpenAPI document. |
| weftfetchtarget | string(uri)¦null | false | none | Base URL the weft_fetch MCP tool should target for this agent. |
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 | SEARCHUPSTREAMERROR |
| error | SEARCHBACKENDMISCONFIGURED |
FetchRequest
{
"url": "https://x402.api.agentmail.to/v0/inboxes",
"max_cost_usd": "0.05",
"method": "GET",
"body": "string",
"headers": {
"Accept": "application/json",
"User-Agent": "my-agent/1.0"
}
}
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 | Hard ceiling on what the buyer is willing to pay. Defaults to 0.10 USD. |
| 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 |
Enumerated Values
| Property | Value |
|---|---|
| method | GET |
| method | POST |
| method | PUT |
| method | PATCH |
| method | DELETE |
FetchResponse
{
"status": 200,
"headers": {
"property1": "string",
"property2": "string"
},
"body_base64": "string",
"paid_usd": "0.002",
"tx_hash": "string",
"artifact_id": 0,
"merchant": {
"address": "string",
"settlement_count": 0,
"first_seen_at": "2019-08-24T14:15:22Z",
"dispute_count": 0
}
}
Successful fetch envelope. body_base64 is the upstream artifact
bytes, base64-encoded. paid_usd, tx_hash, and merchant are
populated only when the upstream charged for the response.
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| status | integer | true | none | HTTP status returned by the upstream after the paid replay. |
| 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 | true | none | USD amount actually settled. Null for free upstreams. |
| tx_hash | string¦null | true | none | Settlement transaction hash. Null for free upstreams. |
| artifact_id | integer¦null | true | none | Internal artifact identifier if the response was persisted; null otherwise. |
| merchant | Merchant¦null | true | none | Merchant reputation snapshot. Null for free upstreams. |
Merchant
{
"address": "string",
"settlement_count": 0,
"first_seen_at": "2019-08-24T14:15:22Z",
"dispute_count": 0
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| address | string | true | none | EVM address that received the payment. |
| settlement_count | integer | true | none | All-time settlement count for this merchant address. |
| firstseenat | string(date-time)¦null | true | none | First time Weft observed a payment to this address. |
| dispute_count | integer | true | none | All-time dispute count for this merchant address. |
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",
"tempo_usd": "3.00",
"total_usd": "15.34",
"spent_today_usd": "0.42"
},
"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. |
| 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_URL |
| error | EXCEEDEDMAXCOST |
| error | INSUFFICIENT_BALANCE |
| error | CONVERSION_UNAVAILABLE |
| error | MERCHANTRETURNEDNON_402 |
| error | ARTIFACTTOOLARGE |
| error | DENYLISTED_RECIPIENT |
| error | WALLETENVIRONMENTMISMATCH |
| error | SETTLEMENT_FAILED |
| error | UNSUPPORTEDPAYMENTMETHOD |
| error | POLICYVIOLATIONMAX_TX |
| error | POLICYVIOLATIONDAILY |
| error | POLICYVIOLATIONWEEKLY |
FetchBalanceSnapshot
{
"promo_usd": "0.00",
"wallet_usdc": "12.34",
"tempo_usd": "3.00",
"total_usd": "15.34",
"spent_today_usd": "0.42"
}
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 | true | none | Live Base USDC balance. |
| tempo_usd | string¦null | true | none | Aggregated USD of allowlisted Tempo dollar tokens, 2dp. null when UNKNOWN (RPC read failed or no token allowlisted for the paired chain) — never "0.00" for an unread component. |
| total_usd | string¦null | true | none | Aggregated USD balance = Base USDC + Tempo dollar tokens, 2dp. Equals wallet_usdc alone when tempo_usd is null. Null when the Base USDC provider is unreachable. |
| spenttodayusd | string | true | none | none |