Skip to content
LogoLogo

Pay with the SDK

Use this path when your agent holds its own keypair and the owner has registered that public address as a delegated signer on a spend account.

Package: @cmdoss/suipay-buyer. Constructor: createPayer.

1. Install

pnpm add @cmdoss/suipay-buyer
npm install @cmdoss/suipay-buyer

2. Get credentials from the console

The owner must already have:

  1. Funded a spend account for the asset.
  2. Registered this key's public Sui address and signed create_grant.

Generate the keypair in the agent runtime. Share only the public address. Load the secret from your environment or secret store — never from this page.

// Step 1 of the SDK path: the delegate key. Included by guides/pay-with-sdk.
import {  } from '@mysten/sui/keypairs/ed25519'
 
// Persist this once. Sui Agent Payments cannot recover it.
const  = .(new (32))
 
export {  }

3. Configure the payer

import {  } from '@cmdoss/suipay-buyer'
 
const  = await ({
  : 'https://gateway.example',
  , // Ed25519Keypair; never leaves this process
  // select: { coinType: '0x...::usdc::USDC' }  // when the key holds several grants
})
if (!.) throw new (..)
 
export {  }

createPayer reads GET /v1/delegate/deployment, signs GET /v1/delegate/grants with the key, and then reads the grant object with an unsigned getObject against a public fullnode — refusing the session unless the chain agrees the grant names this delegate and draws on the named spend account.

The gateway's HMAC secret is not an agent input. It authenticates the gateway to itself.

4. First paid call

const  = await ..(
  'https://gateway.example/v1/image/generate',
  {
    : 'POST',
    : .({ : 'A blue ceramic robot' }),
    : 'application/json',
    : { : '0x...::usdc::USDC', : '50000' },
  },
)
 
export {  }

intent is required and is what bounds the payment: asset, an inclusive maxAmount in atomic units, and optionally the exact recipient. An offer outside it is refused before anything is reserved or signed.

The payer then builds spend_account::settle_policy_payment with the delegate as sender, executes, and replays with the MPP proof.

5. Handle the result

// The shape a paid call returns, bound to the real type rather than described
// in prose. Included by guides/pay-with-sdk. If PayResult loses or renames a
// field, this stops compiling and the page documenting it fails the build.
import type {  } from '@cmdoss/suipay-buyer'
 
declare const : 
 
// `status` is how resolved the outcome is:
//   'ok'        settled AND delivered
//   'ambiguous' something after the signature is unresolved: reconcile, do not retry
//   'failed'    nothing was signed, or the chain rejected the settlement
const : 'ok' | 'ambiguous' | 'failed' = .
 
// `paid` answers "did money leave the grant" and has THREE states, not two.
// It is not a delivery flag: a settled payment whose delivery then failed is
// paid: true with status: 'ambiguous' and a settlement block.
const : boolean | 'unknown' = .
 
// 'unknown' means a signature left this process and finality did not resolve.
// The money may have moved. Treating it as false and retrying pays twice.
// Note `if (!result.paid)` is FALSE for 'unknown', so the falsy check alone
// does not catch it — reconcile on paymentId instead.
if (. === 'unknown') {
  const  = . // === offer.challengeId
  void 
}
 
// Present on every paid result, including a settled pay that failed delivery.
const  = .
// May be the broadcast-unknown sentinel on digestless ambiguity.
const  = .
 
export { , , ,  }

On ambiguous or timeout after possible broadcast: reconcile the same paymentId / digest. Do not open a second payment for the same work unit.

What the payer enforces (order is load-bearing)

  1. Bound the offer first — the agent's own intent. Nothing else runs before it.
  2. Request-binding and offer-expiry checks.
  3. Atomic reserve on the payment-attempt ledger (payment_id = challenge id). Never sign without a durable claim.
  4. Execution capability (submitter present).
  5. Derive payment_id_hash = SHA-256(challengeId) and terms_hash = SHA-256(offer.digest).
  6. Build a complete executable pay transaction (real shared-object versions).
  7. Validate-before-sign (Move call matches intended payment; gas may differ).
  8. Sign as the delegated key, execute, complete the ledger, present MPP proof, settle.

On-chain single-use is the account-scoped payment_id_hash replay marker inside pay.

Gas options

ModeConfiguration
Agent gasThe delegate owns SUI coin objects the payer can attach
SponsoredThe payer signs a personal message with the delegated key (sponsored gas)

Default: agent gas when the delegate has payment objects, else sponsored when the gateway will sponsor.

Pay x402 from a spend account

MPP above is the default. Pass dialect: 'x402' to pay() to settle the same spend account over the x402 dialect instead: the delegate key signs a transaction rather than presenting an MPP proof.

Who this is for: agents paying a resource whose catalog entry advertises x402 on the spend-account rail, on a deployment where the operator has turned on SUIPAY_SPEND_ACCOUNT_X402 (alias SUIPAY_SHARED_POOL_X402). The flag defaults off and is not enabled on any current deployment — enabling it is an operator decision.

Prerequisites:

  • The gateway has the flag on and is wired for it: a local sponsor key, a gRPC network, and a funded gas-lease pool.
  • The resource's registration advertises x402 — otherwise the gateway answers PROOF_REQUIRED, the same as with the flag off.
  • Your grant is scoped to a recipient. Open-recipient grants are refused over x402.
  • Your delegate key is ed25519. zkLogin and multisig delegates are not supported for x402 settlement.

What happens: the payer reads the x402 offer from the 402, builds the same settle_policy_payment transaction as the MPP path above but with gas pinned by the gateway's offer — the sponsor pays gas, not you — signs it with the delegate key, and sends it as PAYMENT-SIGNATURE instead of executing it. The gateway co-signs and submits. There is no client-side execute or finality poll: presenting the signed credential is the settle.

Refused before anything is signed:

  • No x402 offer on the 402, or a missing sponsor-gas pin (the gateway couldn't lease a sponsor coin) → X402_UNAVAILABLE.
  • An open-recipient grant → UNSUPPORTED_SCOPE.

Ambiguous outcomes reconcile the same way as MPP. A 503 is re-presented a bounded number of times; if it still doesn't resolve, the call ends ambiguous with paid: 'unknown' and the settlement digest, so you reconcile on paymentId / digest exactly as in "Handle the result" above — never open a second payment for the same work unit.

Every 402 minted for an x402-eligible resource leases a sponsor gas coin, even if you end up paying over MPP — bounded by the aggregate gas-lease cap.

What not to use

  • createPaidFetch / paidFetch: V1 allowance rail, retired for new agent work.
  • x402 transaction-submit on the spend-account rail when the deployment has not enabled SUIPAY_SPEND_ACCOUNT_X402, or the resource does not advertise x402: refused (PROOF_REQUIRED). The delegated payer falls back to MPP proof. See "Pay x402 from a spend account" above for when it is available.

Next