Getting Started

Mint an Agent

Last updated March 27, 2026

Register an AI agent onchain in a single call using the Metaplex API and the mpl-agent-registry SDK.

Summary

The Metaplex API provides a hosted endpoint that stores agent metadata and returns an unsigned Solana transaction. Signing and submitting that transaction creates an MPL Core asset representing the agent and registers an Agent Identity PDA in a single atomic operation.

  • Creates an MPL Core asset and registers the Agent Identity PDA together in one transaction — no pre-existing asset required
  • Hosted API at https://api.metaplex.com handles metadata storage — no separate upload step before minting
  • Two SDK functionsmintAndSubmitAgent for a one-call flow, mintAgent for manual signing control
  • Multi-network — supports Solana mainnet and devnet, Eclipse, Sonic, and Fogo
  • Requires @metaplex-foundation/mpl-agent-registry v0.2.0+

What You'll Build

A registered onchain AI agent: an MPL Core asset with a linked Agent Identity PDA, created via the Metaplex API and the mpl-agent-registry SDK.

Quick Start

  1. Understand the flow
  2. Install the SDK
  3. Configure a Umi instance
  4. Mint and register in one call
  5. Verify the result

How It Works

Minting an agent through the Metaplex API is a three-step flow orchestrated by the SDK:

  1. API call — The SDK sends your agent details to POST /v1/agents/mint on https://api.metaplex.com. The API stores the agentMetadata off-chain and constructs an unsigned Solana transaction.
  2. Unsigned transaction returned — The API returns the transaction without signing it. Your private key never leaves your environment — the API only builds the instruction set.
  3. Sign and submit — You (or mintAndSubmitAgent automatically) sign the transaction with your keypair and submit it. Onchain, this creates the Core asset and registers the Agent Identity PDA in a single atomic operation.

Two fields, two destinations

When calling mintAndSubmitAgent or mintAgent, you provide two distinct pieces of metadata:

FieldWhere it's storedPurpose
uriOnchain, in the Core asset's metadataPoints to a publicly hosted JSON file — the agent's NFT metadata. Works like any standard Core asset URI.
agentMetadataOff-chain, stored by the Metaplex APIDescribes the agent's capabilities, services, and trust model. Indexed by the registry for discovery.

Both are set during minting and cannot be changed independently after the fact without updating the agent.

This guide creates a new Core asset and registers the agent identity together in one transaction. If you already own a Core asset and only want to attach an identity to it, use registerIdentityV1 instead.

Prerequisites

The following are required before minting:

  • Node.js 18 or later
  • A funded Solana wallet keypair — this wallet pays for the transaction and becomes the agent owner
  • A publicly accessible uri for the Core asset's NFT metadata JSON

Installation

Install the three required packages: the Agent Registry SDK, the core Umi framework, and the default Umi bundle that provides an RPC client and transaction sender.

Terminal
npm install @metaplex-foundation/mpl-agent-registry @metaplex-foundation/umi @metaplex-foundation/umi-bundle-defaults

Umi Setup

Umi is the Metaplex JavaScript framework used to interact with Solana programs. Configure it with your RPC endpoint and keypair before calling any SDK function.

The mplAgentIdentity() plugin registers the Agent Identity program's instruction builders and account deserializers with your Umi instance. Without it, Umi cannot construct or read Agent Identity program instructions.

setup.ts
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { keypairIdentity } from '@metaplex-foundation/umi';
import { mplAgentIdentity } from '@metaplex-foundation/mpl-agent-registry';
// Point Umi at your preferred RPC
const umi = createUmi('https://api.mainnet-beta.solana.com')
.use(mplAgentIdentity());
// Load your keypair — this wallet pays for the transaction and becomes the agent owner
const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes);
umi.use(keypairIdentity(keypair));

The example above uses keypairIdentity — loading a raw secret key directly into Umi. This is the standard approach for server-side scripts and backend integrations. Umi also supports two other identity patterns depending on your environment:

ApproachHowBest for
Raw keypair (this example)keypairIdentity + createKeypairFromSecretKeyServer-side scripts, backends
Filesystem walletcreateSignerFromKeypair + signerIdentity with a JSON key fileLocal development and CLI tools
Browser wallet adapterwalletAdapterIdentity from umi-signer-wallet-adaptersWeb dApps with Phantom, Backpack, etc.

See Connecting a Wallet in the Umi docs for full code examples of each approach, including how to load a filesystem keypair from a .json file and how to wire up a wallet adapter.

Mint and Submit an Agent

mintAndSubmitAgent calls the Metaplex API, signs the returned transaction, and submits it to the network in one step. Use this for most integrations.

mintAndSubmitAgent.ts
1import { mintAndSubmitAgent, mplAgentIdentity } from '@metaplex-foundation/mpl-agent-registry'
2import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
3import { keypairIdentity } from '@metaplex-foundation/umi'
4
5const umi = createUmi('https://api.mainnet-beta.solana.com')
6 .use(mplAgentIdentity())
7
8const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes)
9umi.use(keypairIdentity(keypair))
10
11const result = await mintAndSubmitAgent(umi, {}, {
12 wallet: umi.identity.publicKey,
13 name: 'My AI Agent',
14 uri: 'https://example.com/agent-metadata.json', // Core asset NFT metadata URI
15 agentMetadata: { // Stored off-chain by the Metaplex API
16 type: 'agent',
17 name: 'My AI Agent',
18 description: 'An autonomous trading agent',
19 services: [
20 { name: 'trading', endpoint: 'https://myagent.ai/trade' },
21 ],
22 registrations: [],
23 supportedTrust: [],
24 },
25})
26
27console.log('Asset address:', result.assetAddress)
28console.log('Transaction signature:', result.signature)
29
30// Asset address: <base58 address>
31// Transaction signature: <base58 signature>

Mint an Agent with Manual Signing

mintAgent returns the unsigned transaction without submitting it. Use this when you need to add priority fees, use a hardware wallet, or integrate custom retry logic.

mintAgent.ts
1import {
2 mintAgent,
3 signAndSendAgentTransaction,
4 mplAgentIdentity,
5} from '@metaplex-foundation/mpl-agent-registry'
6import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
7import { keypairIdentity } from '@metaplex-foundation/umi'
8
9const umi = createUmi('https://api.mainnet-beta.solana.com')
10 .use(mplAgentIdentity())
11
12const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes)
13umi.use(keypairIdentity(keypair))
14
15// Step 1: Call the API — returns the unsigned transaction and the pre-computed asset address
16const mintResult = await mintAgent(umi, {}, {
17 wallet: umi.identity.publicKey,
18 name: 'My AI Agent',
19 uri: 'https://example.com/agent-metadata.json',
20 agentMetadata: {
21 type: 'agent',
22 name: 'My AI Agent',
23 description: 'An autonomous trading agent',
24 services: [
25 { name: 'trading', endpoint: 'https://myagent.ai/trade' },
26 { name: 'analysis', endpoint: 'https://myagent.ai/analyze' },
27 ],
28 registrations: [
29 { agentId: 'agent-123', agentRegistry: 'my-registry' },
30 ],
31 supportedTrust: ['tee'],
32 },
33})
34
35console.log('Asset address:', mintResult.assetAddress)
36
37// Step 2: Sign and send using the SDK helper
38const signature = await signAndSendAgentTransaction(umi, mintResult)
39console.log('Confirmed signature:', signature)
40
41// Asset address: <base58 address>
42// Confirmed signature: <base58 signature>

Verify the Result

After minting, confirm the agent identity was registered by fetching the Core asset and checking the AgentIdentity plugin. A successful registration attaches lifecycle hooks for Transfer, Update, and Execute — these are the signals to check.

verifyRegistration.ts
1import { fetchAsset } from '@metaplex-foundation/mpl-core'
2import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
3import { publicKey } from '@metaplex-foundation/umi'
4import { mplAgentIdentity } from '@metaplex-foundation/mpl-agent-registry'
5
6const umi = createUmi('https://api.mainnet-beta.solana.com')
7 .use(mplAgentIdentity())
8
9// Replace with the assetAddress returned by mintAndSubmitAgent or mintAgent
10const assetAddress = publicKey('YOUR_ASSET_ADDRESS')
11const assetData = await fetchAsset(umi, assetAddress)
12const agentIdentity = assetData.agentIdentities?.[0]
13
14console.log('Registration URI:', agentIdentity?.uri)
15console.log('Transfer hook active:', agentIdentity?.lifecycleChecks?.transfer)
16console.log('Update hook active:', agentIdentity?.lifecycleChecks?.update)
17console.log('Execute hook active:', agentIdentity?.lifecycleChecks?.execute)
18
19// Registration URI: https://example.com/agent-metadata.json
20// Transfer hook active: { __kind: 'Listen' }
21// Update hook active: { __kind: 'Listen' }
22// Execute hook active: { __kind: 'Listen' }

If agentIdentities is undefined or empty, the identity was not registered — the transaction may have failed silently or not confirmed. Check the transaction signature onchain before retrying.

Agent Metadata Fields

The agentMetadata object is sent to the Metaplex API and stored off-chain alongside the agent record. It is separate from the Core asset's uri (the NFT metadata file) — see How It Works for the distinction.

FieldTypeRequiredDescription
typestringYesSchema identifier. Use 'agent'.
namestringYesAgent display name
descriptionstringYesWhat the agent does and how to interact with it
servicesAgentService[]NoService endpoints the agent exposes
registrationsAgentRegistration[]NoLinks to external registry entries
supportedTruststring[]NoTrust mechanisms supported — e.g. 'tee', 'reputation'

Agent Service Fields

Each entry in services describes one way to interact with the agent.

FieldTypeRequiredDescription
namestringYesService type — e.g. 'trading', 'chat', 'MCP', 'A2A'
endpointstringYesURL where the service can be reached

Supported Networks

Pass the network value in the input object. It defaults to 'solana-mainnet' when omitted. Make sure your Umi RPC endpoint matches the network you select.

Networknetwork Value
Solana Mainnetsolana-mainnet (default)
Solana Devnetsolana-devnet
Localnetlocalnet
Eclipse Mainneteclipse-mainnet
Sonic Mainnetsonic-mainnet
Sonic Devnetsonic-devnet
Fogo Mainnetfogo-mainnet
Fogo Testnetfogo-testnet

Devnet Testing

Test your integration on Solana devnet before going to mainnet. Point your Umi instance at the devnet RPC and pass network: 'solana-devnet' so the API registers the agent against the devnet cluster. Agents minted on devnet have separate asset addresses from mainnet and will not appear in mainnet explorers.

devnetTest.ts
1import { mintAndSubmitAgent, mplAgentIdentity } from '@metaplex-foundation/mpl-agent-registry'
2import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
3import { keypairIdentity } from '@metaplex-foundation/umi'
4
5const umi = createUmi('https://api.devnet.solana.com')
6 .use(mplAgentIdentity())
7
8const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes)
9umi.use(keypairIdentity(keypair))
10
11const result = await mintAndSubmitAgent(umi, {}, {
12 wallet: umi.identity.publicKey,
13 network: 'solana-devnet',
14 name: 'Test Agent',
15 uri: 'https://example.com/test-metadata.json',
16 agentMetadata: {
17 type: 'agent',
18 name: 'Test Agent',
19 description: 'A test agent on devnet',
20 services: [],
21 registrations: [],
22 supportedTrust: [],
23 },
24})
25
26console.log('Asset address:', result.assetAddress)
27console.log('Transaction signature:', result.signature)
28
29// Asset address: <base58 address>
30// Transaction signature: <base58 signature>

Custom API Base URL

Target a staging or self-hosted API by passing baseUrl in the config argument (the second parameter to mintAgent or mintAndSubmitAgent). Use this when integrating against a non-production environment.

customApiUrl.ts
1import { mintAgent, mplAgentIdentity } from '@metaplex-foundation/mpl-agent-registry'
2import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
3import { keypairIdentity } from '@metaplex-foundation/umi'
4
5const umi = createUmi('https://api.mainnet-beta.solana.com')
6 .use(mplAgentIdentity())
7
8const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes)
9umi.use(keypairIdentity(keypair))
10
11const result = await mintAgent(
12 umi,
13 { baseUrl: 'https://staging-api.metaplex.com' },
14 {
15 wallet: umi.identity.publicKey,
16 name: 'My Agent',
17 uri: 'https://example.com/metadata.json',
18 agentMetadata: {
19 type: 'agent',
20 name: 'My Agent',
21 description: 'Agent targeting staging API',
22 services: [],
23 registrations: [],
24 supportedTrust: [],
25 },
26 }
27)
28
29console.log('Asset address:', result.assetAddress)
30
31// Asset address: <base58 address>

Custom Transaction Sender

Pass a txSender function as the fourth argument to mintAndSubmitAgent to use your own signing and submission infrastructure. This is the right hook for adding Jito bundle tips, priority fees, or custom confirmation polling.

customSender.ts
1import { mintAndSubmitAgent, mplAgentIdentity } from '@metaplex-foundation/mpl-agent-registry'
2import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
3import { keypairIdentity } from '@metaplex-foundation/umi'
4
5const umi = createUmi('https://api.mainnet-beta.solana.com')
6 .use(mplAgentIdentity())
7
8const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes)
9umi.use(keypairIdentity(keypair))
10
11const result = await mintAndSubmitAgent(
12 umi,
13 {},
14 {
15 wallet: umi.identity.publicKey,
16 name: 'My Agent',
17 uri: 'https://example.com/metadata.json',
18 agentMetadata: {
19 type: 'agent',
20 name: 'My Agent',
21 description: 'Agent with custom transaction sender',
22 services: [],
23 registrations: [],
24 supportedTrust: [],
25 },
26 },
27 {
28 txSender: async (tx) => {
29 const signed = await umi.identity.signTransaction(tx)
30 return myCustomSend(signed)
31 },
32 }
33)
34
35console.log('Asset address:', result.assetAddress)
36console.log('Transaction signature:', result.signature)
37
38// Asset address: <base58 address>
39// Transaction signature: <base58 signature>

Error Handling

The SDK exports typed error guards so you can handle each failure mode explicitly rather than catching a generic error.

errorHandling.ts
1import {
2 mintAgent,
3 isAgentApiError,
4 isAgentApiNetworkError,
5 isAgentValidationError,
6 mplAgentIdentity,
7} from '@metaplex-foundation/mpl-agent-registry'
8import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
9import { keypairIdentity } from '@metaplex-foundation/umi'
10
11const umi = createUmi('https://api.mainnet-beta.solana.com')
12 .use(mplAgentIdentity())
13
14const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes)
15umi.use(keypairIdentity(keypair))
16
17const input = {
18 wallet: umi.identity.publicKey,
19 name: 'My Agent',
20 uri: 'https://example.com/metadata.json',
21 agentMetadata: {
22 type: 'agent',
23 name: 'My Agent',
24 description: 'An autonomous agent',
25 services: [],
26 registrations: [],
27 supportedTrust: [],
28 },
29}
30
31try {
32 const result = await mintAgent(umi, {}, input)
33} catch (err) {
34 if (isAgentValidationError(err)) {
35 // Client-side validation failed before the API was called
36 console.error(`Validation error on field "${err.field}": ${err.message}`)
37 } else if (isAgentApiNetworkError(err)) {
38 // Could not reach the API endpoint
39 console.error('Network error:', err.message, err.cause)
40 } else if (isAgentApiError(err)) {
41 // API responded with a non-2xx status
42 console.error(`API error (${err.statusCode}): ${err.message}`)
43 console.error('Response body:', err.responseBody)
44 } else {
45 throw err
46 }
47}

Common Errors

These are the most frequent failure modes and how to resolve them.

ErrorCauseFix
isAgentValidationErrorA required input field is missing or malformedCheck err.field and ensure all required agentMetadata fields are provided
isAgentApiNetworkErrorThe API endpoint was unreachableVerify network connectivity; inspect err.cause for the underlying error
isAgentApiErrorThe API returned a non-2xx statusInspect err.statusCode and err.responseBody; verify the uri is publicly accessible
Blockhash expiredThe transaction was not submitted before the blockhash expiredCall mintAgent again to get a fresh transaction, then retry submission
agentIdentities empty after mintTransaction confirmed but identity plugin not attachedFetch the transaction receipt to confirm it succeeded; if it failed silently, retry the full mint

Full Example

A complete end-to-end snippet — setup, mint, and verify — ready to copy and run.

fullExample.ts
1import {
2 mintAndSubmitAgent,
3 mplAgentIdentity,
4} from '@metaplex-foundation/mpl-agent-registry'
5import { fetchAsset } from '@metaplex-foundation/mpl-core'
6import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
7import { keypairIdentity } from '@metaplex-foundation/umi'
8
9const umi = createUmi('https://api.mainnet-beta.solana.com')
10 .use(mplAgentIdentity())
11
12const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes)
13umi.use(keypairIdentity(keypair))
14
15// 1. Mint the agent
16const result = await mintAndSubmitAgent(umi, {}, {
17 wallet: umi.identity.publicKey,
18 name: 'My AI Agent',
19 uri: 'https://example.com/agent-metadata.json',
20 agentMetadata: {
21 type: 'agent',
22 name: 'My AI Agent',
23 description: 'An autonomous trading agent',
24 services: [
25 { name: 'trading', endpoint: 'https://myagent.ai/trade' },
26 ],
27 registrations: [],
28 supportedTrust: [],
29 },
30})
31
32console.log('Asset address:', result.assetAddress)
33console.log('Tx signature:', result.signature)
34
35// 2. Verify the agent identity was registered
36const assetData = await fetchAsset(umi, result.assetAddress)
37const agentIdentity = assetData.agentIdentities?.[0]
38
39console.log('Registered:', agentIdentity !== undefined)
40console.log('Registration URI:', agentIdentity?.uri)
41
42// Asset address: <base58 address>
43// Tx signature: <base58 signature>
44// Registered: true
45// Registration URI: https://example.com/agent-metadata.json

Notes

  • mintAndSubmitAgent creates a new Core asset on every call — there is no deduplication. Calling it twice with the same input creates two separate agents at two different asset addresses.
  • The uri field is stored in the Core asset's onchain metadata and must point to a publicly accessible JSON document. If you do not have a hosted metadata URI yet, upload the file to Arweave or another permanent storage provider first.
  • To attach an agent identity to an existing Core asset without creating a new one, use registerIdentityV1 instead.
  • The Metaplex API base URL defaults to https://api.metaplex.com. No API key is required.
  • Minting costs the standard Solana transaction fee plus rent for the Core asset account and the Agent Identity PDA.
  • Requires @metaplex-foundation/mpl-agent-registry v0.2.0+.

FAQ

What is the difference between mintAndSubmitAgent and mintAgent?

mintAndSubmitAgent is a convenience wrapper that calls mintAgent then signs and submits the transaction in one step. Use mintAgent directly when you need manual signing control, a custom transaction sender, or the ability to inspect the transaction before submitting.

What is the difference between minting via the Metaplex API and using registerIdentityV1 directly?

The Metaplex API flow (mintAgent / mintAndSubmitAgent) creates the Core asset and registers the agent identity in a single transaction — no pre-existing Core asset is required. The registerIdentityV1 approach attaches an identity plugin to an MPL Core asset you already own.

What is the difference between the uri field and agentMetadata?

The uri is stored directly in the Core asset's onchain metadata — it should point to a publicly hosted JSON file, just like a standard NFT. The agentMetadata object is sent to the Metaplex API and stored off-chain alongside the agent record. Both are set during minting. See How It Works for the full breakdown.

Do I need to create a Core asset before calling mintAndSubmitAgent?

No. The API creates the Core asset and registers the agent identity together. You only need a wallet address, an agent name, a metadata URI, and the agentMetadata object.

Can I test on devnet before going to mainnet?

Yes. Pass network: 'solana-devnet' in the input and point your Umi instance at https://api.devnet.solana.com.

What happens if the API returns a transaction but the submission fails onchain?

A failed onchain transaction means the Core asset was not created and no identity was registered. Call mintAgent again to get a fresh transaction with a new blockhash, then retry.

Which networks does the Metaplex API support?

Solana Mainnet, Solana Devnet, Localnet, Eclipse Mainnet, Sonic Mainnet, Sonic Devnet, Fogo Mainnet, and Fogo Testnet. See Supported Networks for the exact values to pass.

What does it cost to mint an agent?

Minting costs the standard Solana transaction fee plus rent for the Core asset account and the Agent Identity PDA. There is no additional protocol fee charged by the Metaplex API for minting.

Previous
Skill