Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.usemandate.io/llms.txt

Use this file to discover all available pages before exploring further.

The @mandate/sdk package gives your AI agent everything it needs to call payment-gated APIs without storing credentials or managing payment state. You provide an agent ID and mandate ID; Mandate handles the challenge–proof–retry flow transparently. If the policy denies the transaction, you get a typed error with a machine-readable reason code.

Install the SDK

npm install @mandate/sdk

Initialize the client

import { KyaPaymentsClient } from '@mandate/sdk'

const mandate = new KyaPaymentsClient({
  apiKey: process.env.MANDATE_API_KEY,  // ky_sand_... for sandbox
  baseUrl: process.env.MANDATE_API_URL, // optional, defaults to https://api.kya.dev
})
The client exposes a sandbox boolean that is true when your API key starts with ky_sand_. You can use this to gate behavior in your agent:
if (mandate.sandbox) {
  console.log('Running in sandbox mode — no real charges')
}

Make a paid API call

Pass the target URL and your agent and mandate identifiers to fetchWithPayment. The method, extra headers, and body are all optional.
import { KyaPaymentsClient, PolicyDeniedError, PaymentFailedError } from '@mandate/sdk'

try {
  const response = await mandate.fetchWithPayment(
    'https://api.example.com/premium/data',
    {
      agentId: 'agt_xxx',
      mandateId: 'mnd_xxx',
      method: 'GET',   // optional, defaults to GET
      headers: {},     // optional extra headers
    }
  )
  const data = await response.json()
} catch (error) {
  if (error instanceof PolicyDeniedError) {
    console.error('Payment denied:', error.reason_code, error.detail)
  } else if (error instanceof PaymentFailedError) {
    console.error('Payment failed:', error.message)
  }
}
Each fetchWithPayment call may charge the mandate if the policy approves it. Make sure the agent ID and mandate ID you supply are correct before calling paid endpoints.

What happens under the hood

You do not need to implement any of this — fetchWithPayment handles it for you. Understanding the flow helps with debugging:
  1. The SDK makes an initial request to the target URL without any payment headers.
  2. If the endpoint returns 200, the response is returned immediately — the endpoint is free.
  3. If the endpoint returns 402, the SDK parses the payment challenge from the X-Payment-Challenge header.
  4. The SDK calls POST /v1/policy/evaluate on the Mandate API with your agent ID, mandate ID, merchant domain, amount, and currency. If the policy denies the transaction, PolicyDeniedError is thrown immediately and no payment is made.
  5. If the policy approves, the SDK calls POST /v1/payments/proof to obtain a signed payment proof.
  6. The SDK retries the original request with the X-Payment-Proof header attached.
  7. If the retry returns another 402, PaymentFailedError is thrown.
Your agent never stores or manages payment credentials at any point in this flow.
Call POST /v1/verify-agent before making an actual paid request to run a pre-flight policy check. It runs the same checks as the policy engine but does not create a transaction or charge the mandate — useful for surfacing configuration problems early.

Error handling

All SDK errors extend KyaError. Catch the specific subclasses to handle different failure modes:
ClassWhen it is thrownKey properties
PolicyDeniedErrorThe policy engine returns deniedreason_code, detail, code
PaymentFailedErrorPayment verification fails or a network error occursmessage, code
KyaErrorBase class — catch this to handle any SDK errormessage, code

PolicyDeniedError reason codes

reason_codeMeaning
agent_revokedThe agent does not exist or has been revoked
mandate_expiredThe mandate does not exist, is revoked, or has expired
merchant_not_allowedThe target domain is not in the mandate’s merchant allowlist
amount_exceeds_per_transaction_limitThe requested amount exceeds the per-transaction limit on the mandate
total_budget_exceededThe charge would push the mandate’s total spend over its budget
import { PolicyDeniedError } from '@mandate/sdk'

try {
  await mandate.fetchWithPayment(url, options)
} catch (error) {
  if (error instanceof PolicyDeniedError) {
    switch (error.reason_code) {
      case 'mandate_expired':
        // Notify the user or operator to renew the mandate
        break
      case 'total_budget_exceeded':
        // Stop making calls for this mandate — the budget is exhausted
        break
      default:
        console.error('Policy denied:', error.reason_code, error.detail)
    }
  }
}

Environment variables

VariableRequiredDescription
MANDATE_API_KEYYesYour sandbox (ky_sand_...) or production API key
MANDATE_API_URLNoMandate API base URL; defaults to https://api.kya.dev
You can also pass apiKey and baseUrl directly to the KyaPaymentsClient constructor if you prefer not to use environment variables.