Authorize an agent (bring-your-own key)
This guide is for the owner (or owner automation) who wants a non-custodial SDK/REST agent to spend from their spend account. The agent generates its own keypair and shares only its public Sui address. Sui Agent Payments never holds the agent's private key.
For hosted agents, use MCP OAuth instead.
Prerequisites
- Owner has a funded spend account for the asset(s).
- Owner has defined policies / profiles with the correct service targets.
- Owner has a verified owner session (wallet personal-message or trusted internal hop) for the gateway HTTP API.
- Agent has produced a Sui address (Ed25519 is typical) and will keep the secret key offline.
1. Agent: generate and share only the public address
// Generating the delegate key an owner authorizes. Included by
// guides/authorize-an-agent.
import { } from '@mysten/sui/keypairs/ed25519'
const = new ()
const = .().()
// Persist the secret key in the agent secret store. Share agentAddress only.
export { , }2. Owner: create the delegated signer
POST /v1/delegated-signers
Authorization: /* verified owner session */
Content-Type: application/json
Idempotency-Key: /* or body.idempotencyKey */
{
"agentAddress": "0xAGENT...",
"name": "research-bot",
"profileIds": ["profile-uuid"],
"policyIds": [],
"assetScopes": [
{
"asset": "0x...::usdc::USDC",
"totalBudgetAtomic": "10000000",
"maxPerPaymentAtomic": "250000",
"expiresAt": "2026-12-31T00:00:00.000Z"
}
],
"idempotencyKey": "unique-create-key-1"
}profileIds and/or policyIds resolve to an immutable policy-ID snapshot
before the Move transaction is built. Prefer profiles when your console uses
them. idempotencyKey is required and scopes the whole multi-asset aggregate.
First response: awaiting owner signature
{
"status": "awaiting_owner_signature",
"delegatedSignerId": "ds_...",
"agentAddress": "0xAGENT...",
"name": "research-bot",
"method": "delegated_key",
"idempotentReplay": false,
"grants": [
{
"grantId": "g_...",
"asset": "0x...::usdc::USDC",
"coinType": "0x...::usdc::USDC",
"pool": "0xPOOL...",
"policyIds": ["pol_..."],
"onChainPolicyIds": [1],
"caps": {
"sessionCap": "10000000",
"maxPerPayment": "250000"
},
"expiry": "1798675200000",
"moveTarget": "0xPKG...::spend_account::create_policy_grant",
"txKindBytes": "<base64>",
"sender": "0xOWNER..."
}
]
}A verified owner session gates who may call the API. The session alone does
not create the grant. The owner must sign each create_grant transaction kind
(gas may be sponsored by the platform for the owner wallet).
3. Owner: sign, execute, finalize
For each grant in the response:
- Sign
txKindBytesas the pool owner (sender). - Execute the transaction on Sui.
- Finalize with the digest:
POST /v1/delegated-signers/{delegatedSignerId}/grants/{grantId}/finalize
Content-Type: application/json
{ "digest": "BASE58_OR_DIGEST" }Finalization verifies finality, owner sender, Move target, delegate, caps,
expiry, policy set, and the GrantCreated event before reporting the
grant active. The aggregate is active only when every expected per-asset grant
has verified; partial success is partially_active.
4. Hand the agent a connection manifest (non-secret)
Give the agent enough to pay without an owner session:
- network (for example
sui:testnet) - package id
- pool object id
- grant object id (bound at finalize)
- coin type
- delegate address (the agent's public address)
- policy / target snapshots and caps as needed by your SDK
Never put a private key in the manifest, console copy step, or docs example the agent is meant to paste.
5. Revoke
Terminal stop for a BYO signer:
DELETE /v1/delegated-signers/{id}Returns per-asset revoke_grant kinds. Owner signs and executes each, then:
POST /v1/delegated-signers/{id}/grants/{grantId}/revoke/finalize
Content-Type: application/json
{ "digest": "..." }Pause and resume are owner-signed, same prepare → sign → finalize sequence:
POST /v1/delegated-signers/{id}/pause/prepare
POST /v1/delegated-signers/{id}/grants/{grantId}/pause/finalize
POST /v1/delegated-signers/{id}/resume/prepare
POST /v1/delegated-signers/{id}/grants/{grantId}/resume/finalizeA paused signer cannot pay. Resume restores pay under the existing caps.
List
GET /v1/delegated-signers
GET /v1/delegated-signers/{id}Full request/response field tables: Delegated signers API.