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.comhandles metadata storage — no separate upload step before minting - Two SDK functions —
mintAndSubmitAgentfor a one-call flow,mintAgentfor manual signing control - Multi-network — supports Solana mainnet and devnet, Eclipse, Sonic, and Fogo
- Requires
@metaplex-foundation/mpl-agent-registryv0.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
- Understand the flow
- Install the SDK
- Configure a Umi instance
- Mint and register in one call
- Verify the result
How It Works
Minting an agent through the Metaplex API is a three-step flow orchestrated by the SDK:
- API call — The SDK sends your agent details to
POST /v1/agents/mintonhttps://api.metaplex.com. The API stores theagentMetadataoff-chain and constructs an unsigned Solana transaction. - 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.
- Sign and submit — You (or
mintAndSubmitAgentautomatically) 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:
| Field | Where it's stored | Purpose |
|---|---|---|
uri | Onchain, in the Core asset's metadata | Points to a publicly hosted JSON file — the agent's NFT metadata. Works like any standard Core asset URI. |
agentMetadata | Off-chain, stored by the Metaplex API | Describes 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
urifor 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.
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.
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:
| Approach | How | Best for |
|---|---|---|
| Raw keypair (this example) | keypairIdentity + createKeypairFromSecretKey | Server-side scripts, backends |
| Filesystem wallet | createSignerFromKeypair + signerIdentity with a JSON key file | Local development and CLI tools |
| Browser wallet adapter | walletAdapterIdentity from umi-signer-wallet-adapters | Web 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.
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.
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.
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.
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Schema identifier. Use 'agent'. |
name | string | Yes | Agent display name |
description | string | Yes | What the agent does and how to interact with it |
services | AgentService[] | No | Service endpoints the agent exposes |
registrations | AgentRegistration[] | No | Links to external registry entries |
supportedTrust | string[] | No | Trust mechanisms supported — e.g. 'tee', 'reputation' |
Agent Service Fields
Each entry in services describes one way to interact with the agent.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Service type — e.g. 'trading', 'chat', 'MCP', 'A2A' |
endpoint | string | Yes | URL 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.
| Network | network Value |
|---|---|
| Solana Mainnet | solana-mainnet (default) |
| Solana Devnet | solana-devnet |
| Localnet | localnet |
| Eclipse Mainnet | eclipse-mainnet |
| Sonic Mainnet | sonic-mainnet |
| Sonic Devnet | sonic-devnet |
| Fogo Mainnet | fogo-mainnet |
| Fogo Testnet | fogo-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.
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.
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.
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.
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.
| Error | Cause | Fix |
|---|---|---|
isAgentValidationError | A required input field is missing or malformed | Check err.field and ensure all required agentMetadata fields are provided |
isAgentApiNetworkError | The API endpoint was unreachable | Verify network connectivity; inspect err.cause for the underlying error |
isAgentApiError | The API returned a non-2xx status | Inspect err.statusCode and err.responseBody; verify the uri is publicly accessible |
| Blockhash expired | The transaction was not submitted before the blockhash expired | Call mintAgent again to get a fresh transaction, then retry submission |
agentIdentities empty after mint | Transaction confirmed but identity plugin not attached | Fetch 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.
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
mintAndSubmitAgentcreates 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
urifield 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
registerIdentityV1instead. - 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-registryv0.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.
