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 TypeScript agent a single method — fetchWithPayment — that transparently handles the full 402 payment flow: it checks the mandate policy, requests a cryptographic proof from Mandate, and retries the original request with the signed proof attached. You keep your business logic; the SDK handles the payment handshake.

Installation

npm install @mandate/sdk

KyaPaymentsClient

KyaPaymentsClient is the main entry point. Construct it once and reuse it across requests.
import { KyaPaymentsClient } from '@mandate/sdk'

const client = new KyaPaymentsClient({
  apiKey: process.env.MANDATE_API_KEY!,
})

Constructor options

KyaPaymentsClientOptions
apiKey
string
required
Your Mandate API key. Keys that start with ky_sand_ activate sandbox mode.
baseUrl
string
Mandate API base URL. Defaults to https://api.kya.dev.

Instance properties

PropertyTypeDescription
sandboxbooleantrue when the API key starts with ky_sand_. Read-only.

fetchWithPayment(url, options)

Makes an HTTP request to url, automatically handling a 402 payment challenge if the server requires payment.
const response = await client.fetchWithPayment(
  'https://api.example.com/premium/data',
  { agentId, mandateId }
)

Options

FetchWithPaymentOptions
agentId
string
required
The ID of the agent making the payment.
mandateId
string
required
The mandate that authorizes this payment.
method
string
HTTP method. Defaults to 'GET'.
headers
Record<string, string>
Additional request headers to include on both the initial and retried request.
body
string
Request body, forwarded on both the initial and retried request.

Returns

Promise<Response> — the final HTTP response from the target server.

Request flow

1

Initial request

Sends the request to url with a Content-Type: application/json header and any headers you supply.
2

Non-402 response

If the status is anything other than 402, returns the response immediately with no payment processing.
3

Parse the challenge

Reads and base64url-decodes the X-Payment-Challenge header. Throws PaymentChallengeError if the header is absent or malformed.
4

Evaluate policy

Calls POST /v1/policy/evaluate with the agent ID, mandate ID, merchant domain, amount, currency, and resource URL. Throws PolicyDeniedError if the decision is "denied", or PaymentFailedError if the HTTP call itself fails.
5

Request a payment proof

Calls POST /v1/payments/proof to obtain a signed PaymentProof. Throws PaymentFailedError if proof generation fails.
6

Retried request

Resends the original request with three additional headers: X-Payment-Proof, X-Kya-Agent-Id, and X-Kya-Mandate-Id.
7

Final response

Returns the server’s response. If the server still returns 402, throws PaymentFailedError with the server’s reason field.

Error classes

All error classes are exported from @mandate/sdk.

KyaError

Base class for all Mandate SDK errors. Extends the native Error.
PropertyTypeDescription
codestringMachine-readable error code string.

PolicyDeniedError

Thrown when POST /v1/policy/evaluate returns decision: "denied". Extends KyaError with code: "policy_denied".
PropertyTypeDescription
reason_codeReasonCodeWhy the payment was denied. See codes below.
detailstringOptional human-readable explanation from Mandate.
Reason codes
CodeMeaning
agent_revokedThe agent’s access has been revoked.
mandate_expiredThe mandate is no longer valid.
merchant_not_allowedThe merchant is not on the mandate’s allow-list.
amount_exceeds_per_transaction_limitThe charge exceeds the per-transaction cap.
total_budget_exceededThe mandate’s total spend budget has been reached.

PaymentFailedError

Thrown when payment processing itself fails — for example, if proof generation returns an error or the seller rejects the proof. Extends KyaError with code: "payment_failed".

PaymentChallengeError

Thrown when the X-Payment-Challenge header is missing or cannot be decoded. Extends KyaError with code: "challenge_parse_error".

Lower-level exports

These functions are exported for advanced use cases such as building custom payment flows or server-side challenge generation.
ExportSignatureDescription
parsePaymentChallenge(response: Response) => PaymentChallengeReads and validates the X-Payment-Challenge header from a 402 response.
buildPaymentChallenge(options) => PaymentChallengeConstructs a PaymentChallenge object from amount, currency, and resource.
encodeChallengeHeader(challenge: PaymentChallenge) => stringBase64url-encodes a challenge for use in the X-Payment-Challenge header.
buildProof(options) => Promise<PaymentProof>Calls POST /v1/payments/proof and returns a validated PaymentProof.
encodeProofHeader(proof: PaymentProof) => stringBase64url-encodes a proof for use in the X-Payment-Proof header.

buildPaymentChallenge options

OptionTypeRequiredDescription
amountstringYesAmount to charge, e.g. "0.10".
currencystringYesPayment currency, e.g. "USDC".
resourcestringYesFull URL of the resource being accessed.
schemestringNoPayment scheme. Defaults to "kya-sandbox".
networkstringNoChain/network identifier. Defaults to "sandbox".

buildProof options

OptionTypeRequiredDescription
agentIdstringYesID of the paying agent.
mandateIdstringYesID of the authorizing mandate.
challengePaymentChallengeYesChallenge parsed from the 402 response.
apiKeystringYesYour Mandate API key.
apiUrlstringYesMandate API base URL.

Full example

import {
  KyaPaymentsClient,
  PolicyDeniedError,
  PaymentFailedError,
} from '@mandate/sdk'

const client = new KyaPaymentsClient({
  apiKey: process.env.MANDATE_API_KEY!,
})

async function fetchPremiumData(agentId: string, mandateId: string) {
  try {
    const response = await client.fetchWithPayment(
      'https://api.example.com/premium/data',
      { agentId, mandateId }
    )

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`)
    }

    return await response.json()
  } catch (error) {
    if (error instanceof PolicyDeniedError) {
      console.error(`Payment denied [${error.reason_code}]: ${error.message}`)

      if (error.reason_code === 'mandate_expired') {
        // Prompt the user to renew the mandate
      }
    } else if (error instanceof PaymentFailedError) {
      console.error('Payment processing failed:', error.message)
    }
    throw error
  }
}