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.

createAndRegisterLaunch.ts
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:

FieldTypeDescription
signaturesUint8Array[]Transaction signatures
mintAddressstringCreated token mint address
genesisAccountstringGenesis account PDA address
launch.idstringLaunch ID
launch.linkstringLaunch page URL
token.idstringToken ID
token.mintAddressstringToken mint address

Medium Mode — Custom Transaction Sender

Use createAndRegisterLaunch with a custom txSender callback for scenarios like multisig wallets or custom retry logic.

customTxSender.ts
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.

fullControl.ts
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:

FieldTypeDescription
transactionsTransaction[]Unsigned Umi transactions to sign and send
blockhashBlockhashWithExpiryBlockHeightBlockhash for confirming transactions
mintAddressstringCreated token mint address
genesisAccountstringGenesis account PDA address

registerLaunch returns RegisterLaunchResponse:

FieldTypeDescription
existingboolean?true if launch was already registered
launch.idstringLaunch ID
launch.linkstringLaunch page URL
token.idstringToken ID
token.mintAddressstringToken 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

FieldTypeRequiredDescription
walletPublicKey | stringYesCreator's wallet (signs transactions)
tokenTokenMetadataYesToken metadata
networkSvmNetworkNo'solana-mainnet' (default) or 'solana-devnet'
quoteMintQuoteMintInputNo'SOL' (default) or 'USDC'
launchTypeLaunchTypeYes'project'
launchProjectLaunchInputYesLaunch configuration

TokenMetadata

FieldTypeRequiredDescription
namestringYesToken name, 1–32 characters
symbolstringYesToken symbol, 1–10 characters
imagestringYesImage URL (valid HTTPS URL)
descriptionstringNoMax 250 characters
externalLinksExternalLinksNoWebsite, Twitter, Telegram links
FieldTypeDescription
websitestring?Website URL
twitterstring?Twitter/X handle (@mytoken) or full URL
telegramstring?Telegram handle or full URL

LaunchpoolConfig

FieldTypeDescription
tokenAllocationnumberTokens to sell (portion of 1B total supply)
depositStartTimeDate | stringWhen the deposit period opens (lasts 48 hours)
raiseGoalnumberMinimum quote tokens to raise, in whole units (e.g. 200 SOL)
raydiumLiquidityBpsnumber% of raised funds for Raydium LP, in basis points (2000–10000)
fundsRecipientPublicKey | stringReceives the unlocked portion of raised funds

LockedAllocation (Streamflow Lockup)

Optional locked token schedules can be added via launch.lockedAllocations:

FieldTypeDescription
namestringStream name, max 64 characters (e.g. "Team", "Advisors")
recipientPublicKey | stringLockup recipient wallet
tokenAmountnumberTotal tokens in the locked schedule
vestingStartTimeDate | stringWhen the unlock schedule begins
vestingDuration{ value: number, unit: TimeUnit }Full lockup period
unlockScheduleTimeUnitHow frequently tokens are released
cliffobject?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:

lockedAllocations.ts
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):

FieldTypeDefaultDescription
txSender(txs: Transaction[]) => Promise<Uint8Array[]>Custom transaction sender callback
commitmentstring'confirmed'Commitment level for confirmation
preflightCommitmentstring'confirmed'Preflight commitment level
skipPreflightbooleanfalseSkip 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);
}
}
PropertyTypeDescription
statusCodenumberHTTP status code
responseBodyunknownFull 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);
}
PropertyTypeDescription
causeErrorThe 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);
}
PropertyTypeDescription
fieldstringThe input field that failed validation

Comprehensive Error Handling

errorHandling.ts
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:

RuleConstraint
Token name1–32 characters
Token symbol1–10 characters
Token imageValid HTTPS URL
Token descriptionMax 250 characters
Token allocationGreater than 0
Raise goalGreater than 0
Raydium liquidity BPS2000–10000 (20%–100%)
Total supplyFixed at 1 billion tokens
Locked allocation nameMax 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