SDK
API Client
Last updated February 19, 2026
The Genesis API client provides high-level functions for creating and registering token launches. It handles transaction building, signing, and on-chain registration through a simple interface built on Umi.
We recommend using the SDK to create launches programmatically, as metaplex.com does not yet support the full feature set of the Genesis program. Mainnet launches created through the API will appear on metaplex.com once registered.
Installation
npm install @metaplex-foundation/genesis @metaplex-foundation/umi \
@metaplex-foundation/umi-bundle-defaults
Setup
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { genesis } from '@metaplex-foundation/genesis';
import { keypairIdentity } from '@metaplex-foundation/umi';
const umi = createUmi('https://api.mainnet-beta.solana.com')
.use(genesis());
// For server-side or scripts, load a keypair
umi.use(keypairIdentity(myKeypair));
Three Integration Modes
The SDK offers three modes for creating launches, from fully automatic to fully manual.
Easy Mode — createAndRegisterLaunch
The simplest approach. One function call handles everything: creates the on-chain accounts, signs and sends transactions via Umi, and registers the launch.
1import {
2 createAndRegisterLaunch,
3 CreateLaunchInput,
4 genesis,
5} from '@metaplex-foundation/genesis'
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(genesis())
11
12// Use keypairIdentity to set a wallet when running server-side:
13// umi.use(keypairIdentity(myKeypair))
14
15const input: CreateLaunchInput = {
16 wallet: umi.identity.publicKey,
17 token: {
18 name: 'My Token',
19 symbol: 'MTK',
20 image: 'https://gateway.irys.xyz/...',
21 },
22 launchType: 'project',
23 launch: {
24 launchpool: {
25 tokenAllocation: 500_000_000,
26 depositStartTime: new Date(Date.now() + 48 * 60 * 60 * 1000),
27 raiseGoal: 200,
28 raydiumLiquidityBps: 5000,
29 fundsRecipient: umi.identity.publicKey,
30 },
31 },
32}
33
34const result = await createAndRegisterLaunch(umi, {}, input)
35console.log(`Launch live at: ${result.launch.link}`)
Returns CreateAndRegisterLaunchResult:
| Field | Type | Description |
|---|---|---|
signatures | Uint8Array[] | Transaction signatures |
mintAddress | string | Created token mint address |
genesisAccount | string | Genesis account PDA address |
launch.id | string | Launch ID |
launch.link | string | Launch page URL |
token.id | string | Token ID |
token.mintAddress | string | Token mint address |
Medium Mode — Custom Transaction Sender
Use createAndRegisterLaunch with a custom txSender callback for scenarios like multisig wallets or custom retry logic.
1import {
2 createAndRegisterLaunch,
3 SignAndSendOptions,
4} from '@metaplex-foundation/genesis'
5
6// Assumes umi and input from the Easy Mode example.
7
8const options: SignAndSendOptions = {
9 txSender: async (transactions) => {
10 // Replace myCustomSign / myCustomSend with your own
11 // signing and sending implementation.
12 const signatures: Uint8Array[] = []
13 for (const tx of transactions) {
14 const signed = await myCustomSign(tx) // your signer
15 const sig = await myCustomSend(signed) // your sender
16 signatures.push(sig)
17 }
18 return signatures
19 },
20}
21
22const result = await createAndRegisterLaunch(umi, {}, input, options)
23console.log(`Launch live at: ${result.launch.link}`)
The txSender callback receives the array of unsigned transactions and must return an array of signatures. The SDK handles registration after the callback completes.
Full Control — createLaunch + registerLaunch
For complete control over the transaction lifecycle. You call createLaunch to get unsigned transactions, handle signing and sending yourself, then call registerLaunch.
1import {
2 createLaunch,
3 registerLaunch,
4} from '@metaplex-foundation/genesis'
5
6// Assumes umi and input from the Easy Mode example.
7
8// Step 1: Get unsigned transactions from the API
9const createResult = await createLaunch(umi, {}, input)
10
11// Step 2: Sign and send each transaction
12for (const tx of createResult.transactions) {
13 const signedTx = await umi.identity.signTransaction(tx)
14 const signature = await umi.rpc.sendTransaction(signedTx, {
15 commitment: 'confirmed',
16 preflightCommitment: 'confirmed',
17 })
18 await umi.rpc.confirmTransaction(signature, {
19 commitment: 'confirmed',
20 strategy: {
21 type: 'blockhash',
22 ...createResult.blockhash,
23 },
24 })
25}
26
27// Step 3: Register the launch
28const registerResult = await registerLaunch(umi, {}, {
29 genesisAccount: createResult.genesisAccount,
30 createLaunchInput: input,
31})
32console.log(`Launch live at: ${registerResult.launch.link}`)
createLaunch returns CreateLaunchResponse:
| Field | Type | Description |
|---|---|---|
transactions | Transaction[] | Unsigned Umi transactions to sign and send |
blockhash | BlockhashWithExpiryBlockHeight | Blockhash for confirming transactions |
mintAddress | string | Created token mint address |
genesisAccount | string | Genesis account PDA address |
registerLaunch returns RegisterLaunchResponse:
| Field | Type | Description |
|---|---|---|
existing | boolean? | true if launch was already registered |
launch.id | string | Launch ID |
launch.link | string | Launch page URL |
token.id | string | Token ID |
token.mintAddress | string | Token mint address |
Transactions must be confirmed on-chain before calling registerLaunch. The register endpoint validates that the genesis account exists and matches the expected configuration.
Configuration
CreateLaunchInput
| Field | Type | Required | Description |
|---|---|---|---|
wallet | PublicKey | string | Yes | Creator's wallet (signs transactions) |
token | TokenMetadata | Yes | Token metadata |
network | SvmNetwork | No | 'solana-mainnet' (default) or 'solana-devnet' |
quoteMint | QuoteMintInput | No | 'SOL' (default) or 'USDC' |
launchType | LaunchType | Yes | 'project' |
launch | ProjectLaunchInput | Yes | Launch configuration |
TokenMetadata
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Token name, 1–32 characters |
symbol | string | Yes | Token symbol, 1–10 characters |
image | string | Yes | Image URL (valid HTTPS URL) |
description | string | No | Max 250 characters |
externalLinks | ExternalLinks | No | Website, Twitter, Telegram links |
ExternalLinks
| Field | Type | Description |
|---|---|---|
website | string? | Website URL |
twitter | string? | Twitter/X handle (@mytoken) or full URL |
telegram | string? | Telegram handle or full URL |
LaunchpoolConfig
| Field | Type | Description |
|---|---|---|
tokenAllocation | number | Tokens to sell (portion of 1B total supply) |
depositStartTime | Date | string | When the deposit period opens (lasts 48 hours) |
raiseGoal | number | Minimum quote tokens to raise, in whole units (e.g. 200 SOL) |
raydiumLiquidityBps | number | % of raised funds for Raydium LP, in basis points (2000–10000) |
fundsRecipient | PublicKey | string | Receives the unlocked portion of raised funds |
LockedAllocation (Streamflow Lockup)
Optional locked token schedules can be added via launch.lockedAllocations:
| Field | Type | Description |
|---|---|---|
name | string | Stream name, max 64 characters (e.g. "Team", "Advisors") |
recipient | PublicKey | string | Lockup recipient wallet |
tokenAmount | number | Total tokens in the locked schedule |
vestingStartTime | Date | string | When the unlock schedule begins |
vestingDuration | { value: number, unit: TimeUnit } | Full lockup period |
unlockSchedule | TimeUnit | How frequently tokens are released |
cliff | object? | Optional cliff with duration and unlockAmount |
The vestingStartTime must be after the deposit period ends (i.e., after depositStartTime + 48 hours). The API will reject locked schedules that start before the deposit window closes.
TimeUnit values: 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'WEEK', 'TWO_WEEKS', 'MONTH', 'QUARTER', 'YEAR'
Example with locked allocations:
1import {
2 createAndRegisterLaunch,
3 CreateLaunchInput,
4 genesis,
5} from '@metaplex-foundation/genesis'
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(genesis())
11
12// Use keypairIdentity to set a wallet when running server-side:
13// umi.use(keypairIdentity(myKeypair))
14
15const input: CreateLaunchInput = {
16 wallet: umi.identity.publicKey,
17 token: {
18 name: 'My Token',
19 symbol: 'MTK',
20 image: 'https://gateway.irys.xyz/...',
21 description: 'A project token with locked allocations.',
22 externalLinks: {
23 website: 'https://example.com',
24 twitter: '@mytoken',
25 },
26 },
27 launchType: 'project',
28 launch: {
29 launchpool: {
30 tokenAllocation: 500_000_000,
31 depositStartTime: new Date('2026-04-01T00:00:00Z'),
32 raiseGoal: 200,
33 raydiumLiquidityBps: 5000,
34 fundsRecipient: 'FundsRecipientWallet...',
35 },
36 lockedAllocations: [
37 {
38 name: 'Team',
39 recipient: 'TeamWallet...',
40 tokenAmount: 100_000_000,
41 vestingStartTime: new Date('2026-04-05T00:00:00Z'),
42 vestingDuration: { value: 1, unit: 'YEAR' },
43 unlockSchedule: 'MONTH',
44 cliff: {
45 duration: { value: 3, unit: 'MONTH' },
46 unlockAmount: 10_000_000,
47 },
48 },
49 ],
50 },
51}
52
53const result = await createAndRegisterLaunch(umi, {}, input)
54console.log(`Launch live at: ${result.launch.link}`)
SignAndSendOptions
Options for createAndRegisterLaunch (extends RpcSendTransactionOptions):
| Field | Type | Default | Description |
|---|---|---|---|
txSender | (txs: Transaction[]) => Promise<Uint8Array[]> | — | Custom transaction sender callback |
commitment | string | 'confirmed' | Commitment level for confirmation |
preflightCommitment | string | 'confirmed' | Preflight commitment level |
skipPreflight | boolean | false | Skip preflight checks |
Error Handling
The SDK provides three error types with type guard functions.
GenesisApiError
Thrown when the API returns a non-success response.
import { isGenesisApiError } from '@metaplex-foundation/genesis';
try {
await createLaunch(umi, {}, input);
} catch (err) {
if (isGenesisApiError(err)) {
console.error('API error:', err.statusCode, err.responseBody);
}
}
| Property | Type | Description |
|---|---|---|
statusCode | number | HTTP status code |
responseBody | unknown | Full response body from the API |
GenesisApiNetworkError
Thrown when the fetch call fails (network issue, DNS failure, etc.).
import { isGenesisApiNetworkError } from '@metaplex-foundation/genesis';
if (isGenesisApiNetworkError(err)) {
console.error('Network error:', err.cause.message);
}
| Property | Type | Description |
|---|---|---|
cause | Error | The underlying fetch error |
GenesisValidationError
Thrown when input validation fails before making an API call.
import { isGenesisValidationError } from '@metaplex-foundation/genesis';
if (isGenesisValidationError(err)) {
console.error(`Validation failed on field "${err.field}":`, err.message);
}
| Property | Type | Description |
|---|---|---|
field | string | The input field that failed validation |
Comprehensive Error Handling
1import {
2 createAndRegisterLaunch,
3 isGenesisApiError,
4 isGenesisApiNetworkError,
5 isGenesisValidationError,
6} from '@metaplex-foundation/genesis'
7
8// Assumes umi and input from the Easy Mode example.
9
10try {
11 const result = await createAndRegisterLaunch(umi, {}, input)
12 console.log(`Launch live at: ${result.launch.link}`)
13} catch (err) {
14 if (isGenesisValidationError(err)) {
15 console.error(`Invalid input "${err.field}":`, err.message)
16 } else if (isGenesisApiError(err)) {
17 console.error('API error:', err.statusCode, err.responseBody)
18 } else if (isGenesisApiNetworkError(err)) {
19 console.error('Network error:', err.cause.message)
20 } else {
21 throw err
22 }
23}
Validation Rules
The SDK validates inputs before sending them to the API:
| Rule | Constraint |
|---|---|
| Token name | 1–32 characters |
| Token symbol | 1–10 characters |
| Token image | Valid HTTPS URL |
| Token description | Max 250 characters |
| Token allocation | Greater than 0 |
| Raise goal | Greater than 0 |
| Raydium liquidity BPS | 2000–10000 (20%–100%) |
| Total supply | Fixed at 1 billion tokens |
| Locked allocation name | Max 64 characters |
The total supply is always 1 billion tokens. The SDK automatically calculates the creator allocation as the remainder after subtracting the launchpool, Raydium LP, and any locked allocations.
Helper Functions
signAndSendLaunchTransactions
If you want the default sign-and-send behavior as a standalone function (useful for retries or partial flows):
import {
createLaunch,
signAndSendLaunchTransactions,
} from '@metaplex-foundation/genesis';
const createResult = await createLaunch(umi, {}, input);
const signatures = await signAndSendLaunchTransactions(umi, createResult, {
commitment: 'confirmed',
});
Transactions are signed and sent sequentially — each is confirmed before the next is sent.
buildCreateLaunchPayload
Validates input and builds the raw API payload. Exported for advanced use cases:
import { buildCreateLaunchPayload } from '@metaplex-foundation/genesis';
const payload = buildCreateLaunchPayload(input);
// Use payload with your own HTTP client
