Leash agents follow Metaplex’s MIP-104 / Agent Identity standard: a single MPL Core asset with an AgentIdentity plugin attached. @leash/registry-utils ships a thin wrapper around mintAndSubmitAgent that does both in one transaction via the hosted Metaplex API, so you don’t have to sequence two instructions yourself.

When to use this vs. BYO

FlowWhat you getUse when
createAgentCore asset + Agent Identity + off-chain agentMetadata stored by api.metaplex.com — one signed txYou’re starting from scratch and want the simplest path
registerAgentIdentity (BYO)Attaches an Agent Identity to an MPL Core asset you already own; you host the registration JSON yourselfYou already minted the asset, or you need full custody of the registration document (Arweave, IPFS gateway, your own HTTPS host)
Both paths produce the same on-chain shape: an AgentIdentityV1 PDA derived from the asset’s pubkey, with Listen lifecycle hooks on Transfer, Update, and Execute. The only difference is who hosts the registration JSON.

Pick who signs

createAgent doesn’t care how umi.identity is set up — only that it can sign. There are three common choices, all wire-compatible with the rest of the SDK:
SignerWhere it livesUse when
Privy embedded walletThe user’s browser, recoverable via email / OAuthYou’re building a browser-first agent with a human user (the playground default)
BYO local keypairA solana-keygen JSON file you back up yourselfHeadless scripts, CI, long-lived agents whose owner key shouldn’t depend on a third party
Server env var (LEASH_DEV_PAYER_SECRET_KEY)Process memory on a Node serverCron, internal tools, the playground’s headless POST /api/agents/create route
Whichever you pick, the wallet that signs the mint becomes the agent owner — the only key authorised to call mpl-core::Execute against the asset (withdraw, set delegation, set agent token, etc.). For the full mental model — owner vs. executive vs. operator — see Identities. For the non-Privy paths in detail, see Bring your own keypair.

SDK call

import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { keypairIdentity } from '@metaplex-foundation/umi';
import { mplAgentIdentity } from '@metaplex-foundation/mpl-agent-registry';
import { createAgent } from '@leash/registry-utils';

const umi = createUmi('https://api.devnet.solana.com').use(mplAgentIdentity());
umi.use(keypairIdentity(umi.eddsa.createKeypairFromSecretKey(secretKeyBytes)));

const { assetAddress, signature, network } = await createAgent(umi, {
  wallet: String(umi.identity.publicKey),
  network: 'solana-devnet',
  name: 'Plexpert',
  description: 'An informational agent for Metaplex protocols.',
  uri: 'https://example.com/agent-metadata.json', // on-chain Core metadata (public JSON)
  services: [{ name: 'web', endpoint: 'https://example.com/agent' }],
  supportedTrust: ['reputation'],
});
Returns { assetAddress, signature, network }. The signature is base58-encoded. The wallet supplied via umi.identity pays for the transaction and becomes the agent owner. Match the Umi RPC to the network you pass.

Two-step (manual signing)

When you need priority fees, a hardware wallet, or a custom retry loop:
import { prepareAgentMint, sendPreparedAgentMint } from '@leash/registry-utils';

const prepared = await prepareAgentMint(umi, {
  /* same input */
});
// inspect prepared.transaction, add priority fees, etc.
const signature = await sendPreparedAgentMint(umi, prepared);
Every other mutating helper in @leash/registry-utils (setSpendDelegation, withdrawTreasury, delegateExecution, …) ships a matching prepare* function that returns an unsigned TransactionBuilder plus echo fields, so the same pattern works end-to-end. See Prepare/Send split for the full surface.

After minting

Once the transaction confirms, use the read helpers from the same package to verify the result:
import { getAgentIdentityStatus } from '@leash/registry-utils';

const status = await getAgentIdentityStatus(umi, assetAddress);
// { registered: true, treasury: '<asset-signer-pda>', account: { ... } }
The treasury field is the Asset Signer PDA — the agent’s built-in wallet. Anyone can derive it from the asset address; only the asset itself can sign for it via Core’s Execute instruction.

Run the agent

To let an off-chain executive sign Execute instructions on the agent’s behalf you need two more on-chain calls:
import { registerExecutive, delegateExecution } from '@leash/registry-utils';

await registerExecutive(umi); // one-time per wallet
await delegateExecution(umi, {
  agentAsset: assetAddress,
  executiveAuthority: String(umi.identity.publicKey),
});
See Run an Agent for the full execution model.

Fund the agent (treasury delegation)

For real x402 calls the executive needs a capped SPL delegation on the agent treasury — that’s how funds debit from the agent (not from your personal wallet) on every settled call. One call:
import { setSpendDelegation } from '@leash/registry-utils';

await setSpendDelegation(umi, {
  agentAsset: assetAddress,
  mint: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU', // devnet USDC
  executive: String(umi.identity.publicKey),
  amount: 5_000_000n, // 5 USDC
});
Full walkthrough — including how to top up, adjust, and revoke — in Fund an agent.

What “Supported trust” means

supportedTrust is the MIP-104 / ERC-8004 field where an agent declares which trust mechanisms back its claims. It’s an open string[], but the four common values are:
ValueMeaning
reputationCommunity feedback / on-chain reputation. Trust by track record.
crypto-economicBonded stake / slashing — misbehaviour costs the operator money.
teeRuns inside a Trusted Execution Environment with verifiable attestation.
zk-proofPublishes zero-knowledge proofs of correct execution.
Pick all that apply. Buyers and registries can filter by these values when selecting agents to interact with, so an empty list is technically allowed but signals “trust nothing about me yet” — fine for prototypes, weak for production. You can also add custom labels (e.g. audit-firm-x, proof-of-humanity) when your trust story doesn’t fit the presets.

In the playground

The Next.js playground (apps/web) wraps all of this in a UI signed by your connected Privy wallet — there is no shared hot key:
  • /agents/new — Create Agent form, two tabs: 1 · Identity & metadata then 2 · Services & session.
    • Tab 1. Name, description, network, and a publicly fetchable JSON URL for the on-chain Core metadata URI (HTTPS, IPFS gateway, Arweave, etc.) — you host that JSON yourself.
    • Tab 2. Service endpoints, supported trust models (checkboxes plus custom values — see “What ‘Supported trust’ means” above), optional behaviour rules, then Create agent. Minting runs createAgent via your Privy wallet; devnet/mainnet flows may also call provisionTreasuryAtas. Executive registration, delegation, and spend caps happen on the agent profile — see Fund an agent.
  • /agents/[mint]Identity tab — resolves the on-chain registration URI from the AgentIdentity plugin.
  • /agents/[mint]Execute tab — registerExecutiveV1 + delegateExecutionV1, same Privy wallet.
  • /agents/[mint]Token (Genesis) tab — paste the token image URL Metaplex accepts, then launch (see Launch an agent token).
Configure NEXT_PUBLIC_PRIVY_APP_ID in apps/web/.env.local, log in via the wallet button (top-right), and fund your embedded wallet on devnet (solana airdrop 1 <pubkey> --url devnet). The bridge is apps/web/lib/privy-umi.tsusePrivyUmi() wraps the Privy ConnectedSolanaWallet in Metaplex’s walletAdapterIdentity so any @leash/registry-utils SDK call works straight from a React component.
The LEASH_DEV_PAYER_SECRET_KEY env variable is optional and only used by the headless server fallback routes (POST /api/agents/create, POST /api/agents/executive) — useful for CI / cron / non-Privy callers, not the demo. Full walkthrough in Bring your own keypair.