This guide walks you through the complete tokenization workflow using the Oumla SDK, from creating collections to minting and burning tokens.
Two Ways to Use Oumla : This guide uses the TypeScript SDK. You can also accomplish the same tasks using direct REST API calls - see the API Reference for endpoint details.
Overview
The tokenization workflow involves several steps:
Create Collection - Deploy an NFT collection contract (ERC-721 or ERC-1155)
Check Status - Monitor the async deployment workflow
Mint Tokens - Mint NFTs to specific addresses
View Tokens - Query tokens in your collection
Burn Tokens - Remove tokens from circulation (optional)
Collection creation and minting use Temporal workflows for async processing. You’ll need to check workflow status to know when operations complete.
Prerequisites
Before starting, ensure you have:
An initialized SDK client
A profile created
An address generated (for contract deployment)
Your client share (for signing operations)
Step 1: Create a Collection
Creating a collection deploys a new NFT contract. This is an async operation that returns a workflow ID.
import { OumlaSdkApiClient , OumlaSdkApiEnvironment } from '@oumla/sdk' ;
const client = new OumlaSdkApiClient ({
environment: OumlaSdkApiEnvironment . Sandbox ,
apiKey: 'your-api-key-here'
});
// Create an ERC-721 collection (NON_FUNGIBLE_TOKEN)
const collection = await client . tokenization . createCollection ({
addressId: 'your-address-id' ,
type: 'NON_FUNGIBLE_TOKEN' ,
clientShare: 'your-client-share' ,
network: 'ETHEREUM' ,
displayName: 'My NFT Collection' ,
useGasless: false ,
fee: '0' ,
feeLevel: 'standard' ,
description: 'Optional collection description' ,
createParams: {
initializeParams: [
{ name: '_name' , type: 'string' , value: 'My Collection' },
{ name: '_symbol' , type: 'string' , value: 'MYC' },
{ name: 'defaultAdmin' , type: 'address' , value: '0x1111111111111111111111111111111111111111' },
{ name: 'minter' , type: 'address' , value: '0x2222222222222222222222222222222222222222' },
{ name: 'pauser' , type: 'address' , value: '0x3333333333333333333333333333333333333333' }
]
}
});
console . log ( 'Workflow ID:' , collection . data . workflowId );
ERC-1155 Collection Example
// Create an ERC-1155 collection (SEMI_FUNGIBLE_TOKEN)
const collection = await client . tokenization . createCollection ({
addressId: 'your-address-id' ,
type: 'SEMI_FUNGIBLE_TOKEN' ,
clientShare: 'your-client-share' ,
network: 'ETHEREUM' ,
displayName: 'My Multi-Token Collection' ,
createParams: {
initializeParams: [
{ name: '_name' , type: 'string' , value: 'My Multi-Token Collection' },
{ name: '_symbol' , type: 'string' , value: 'MMTC' },
{ name: 'defaultAdmin' , type: 'address' , value: '0x1111111111111111111111111111111111111111' },
{ name: 'minter' , type: 'address' , value: '0x2222222222222222222222222222222222222222' },
{ name: 'pauser' , type: 'address' , value: '0x3333333333333333333333333333333333333333' }
]
}
});
Collection Types
NON_FUNGIBLE_TOKEN (ERC-721): Non-fungible tokens (NFTs) - each token is unique
SEMI_FUNGIBLE_TOKEN (ERC-1155): Multi-token standard - supports both fungible and non-fungible tokens
Use NON_FUNGIBLE_TOKEN for ERC-721 collections and SEMI_FUNGIBLE_TOKEN for ERC-1155 collections. These are the actual API values, not ERC721 or ERC1155.
Step 2: Check Workflow Status
Collection creation is async. Check the workflow status to know when deployment completes:
// Check workflow status
const status = await client . temporal . getWorkflowStatus ({
workflowId: collection . data . workflowId
});
console . log ( 'Status:' , status . data . status ); // COMPLETED, RUNNING, FAILED
console . log ( 'Result:' , status . data . result );
if ( status . data . status === 'COMPLETED' ) {
console . log ( 'Collection deployed at:' , status . data . result . contractAddress );
} else if ( status . data . status === 'FAILED' ) {
console . error ( 'Deployment failed:' , status . data . error );
}
Workflow Status Values
RUNNING : Operation in progress
COMPLETED : Operation successful
FAILED : Operation failed (check error details)
CANCELLED : Operation was cancelled
Poll the workflow status endpoint every few seconds until status is COMPLETED or FAILED. Consider implementing exponential backoff for production applications.
Step 3: Get Collection Details
Once deployment completes, retrieve collection details:
const collectionDetails = await client . tokenization . getCollectionById (
collection . collectionId
);
console . log ( 'Collection:' , collectionDetails . data );
console . log ( 'Contract Address:' , collectionDetails . data . contractAddress );
console . log ( 'Network:' , collectionDetails . data . network );
Step 4: Mint Tokens
Mint tokens to specific addresses. For ERC-721, mint unique tokens. For ERC-1155, specify token ID and amount.
Minting ERC-721 Tokens
const mintResult = await client . tokenization . mintToken ({
id: collection . collectionId ,
addressId: 'your-address-id' ,
clientShare: 'your-client-share' ,
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' , // Recipient address
tokenId: '0' , // Sequential token ID (start from 0)
metadata: {
name: 'My First NFT' ,
description: 'This is my first NFT' ,
image: 'https://example.com/image.png' ,
attributes: [
{
trait_type: 'Color' ,
value: 'Blue'
}
]
}
});
console . log ( 'Mint Workflow ID:' , mintResult . mint . workflowId );
Minting ERC-1155 Tokens
const mintResult = await client . tokenization . mintToken ({
id: collection . collectionId ,
addressId: 'your-address-id' ,
clientShare: 'your-client-share' ,
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
tokenId: '1' , // Token type ID
amount: '100' , // Amount to mint (for ERC-1155)
metadataURI: 'https://api.example.com/metadata/1'
});
console . log ( 'Mint Workflow ID:' , mintResult . mint . workflowId );
Token ID Sequencing : For ERC-721 collections, token IDs must be sequential starting from 0. The system validates this to prevent conflicts.
Step 5: Check Mint Status
Minting is also async. Check the workflow status:
const mintStatus = await client . temporal . getWorkflowStatus ({
workflowId: mintResult . data . mint ?. workflowId
});
if ( mintStatus . data . status === 'COMPLETED' ) {
console . log ( 'Token minted successfully!' );
console . log ( 'Transaction Hash:' , mintStatus . data . result . transactionHash );
}
Step 6: View Collection Tokens
List all tokens in your collection:
const tokens = await client . tokenization . getCollectionTokens ({
id: collection . collectionId ,
skip: 0 ,
take: 25
});
console . log ( 'Total tokens:' , tokens . data . tokens . length );
tokens . data . tokens . forEach ( token => {
console . log ( `Token ${ token . tokenId } :` , token );
});
Step 7: Get Token Details
Get details for a specific token:
const tokenDetails = await client . tokenization . getCollectionTokenDetails ({
id: collection . collectionId ,
tokenId: '0'
});
console . log ( 'Token Details:' , tokenDetails . data );
console . log ( 'Owner:' , tokenDetails . data . owner );
console . log ( 'Metadata:' , tokenDetails . data . metadata );
Step 8: Burn Tokens (Optional)
Burn tokens to remove them from circulation:
const burnResult = await client . tokenization . burnToken ( 'collection-id' , {
addressId: 'your-address-id' ,
clientShare: 'your-client-share' ,
tokenId: '0' // Token ID to burn
});
console . log ( 'Burn Workflow ID:' , burnResult . data . burn ?. workflowId );
// Check burn status
const burnStatus = await client . temporal . getWorkflowStatus ({
workflowId: burnResult . data . burn ?. workflowId
});
Complete Example
Here’s a complete example that ties everything together:
import { OumlaSdkApiClient , OumlaSdkApiEnvironment } from '@oumla/sdk' ;
const client = new OumlaSdkApiClient ({
environment: OumlaSdkApiEnvironment . Sandbox ,
apiKey: process . env . OUMLA_API_KEY
});
async function tokenizationWorkflow () {
try {
// Step 1: Create collection
const collection = await client . tokenization . createCollection ({
addressId: process . env . ADDRESS_ID ,
type: 'NON_FUNGIBLE_TOKEN' , // Use 'NON_FUNGIBLE_TOKEN' for ERC-721, 'SEMI_FUNGIBLE_TOKEN' for ERC-1155
clientShare: process . env . CLIENT_SHARE ,
network: 'ETHEREUM' ,
displayName: 'My NFT Collection' ,
createParams: {
initializeParams: [
{ name: '_name' , type: 'string' , value: 'My Collection' },
{ name: '_symbol' , type: 'string' , value: 'MYC' },
{ name: 'defaultAdmin' , type: 'address' , value: process . env . ADMIN_ADDRESS },
{ name: 'minter' , type: 'address' , value: process . env . MINTER_ADDRESS },
{ name: 'pauser' , type: 'address' , value: process . env . PAUSER_ADDRESS }
]
}
});
console . log ( 'Collection creation started. Workflow ID:' , collection . data . workflowId );
// Step 2: Wait for collection deployment
let collectionStatus ;
do {
await new Promise ( resolve => setTimeout ( resolve , 3000 )); // Wait 3 seconds
collectionStatus = await client . temporal . getWorkflowStatus ({
workflowId: collection . data . workflowId
});
console . log ( 'Collection status:' , collectionStatus . data . status );
} while ( collectionStatus . data . status === 'RUNNING' );
if ( collectionStatus . data . status === 'FAILED' ) {
throw new Error ( 'Collection deployment failed: ' + collectionStatus . data . error );
}
const contractAddress = collectionStatus . data . result . contractAddress ;
console . log ( 'Collection deployed at:' , contractAddress );
// Step 3: Get collection ID from workflow result (implementation-specific)
const collectionId = 'collection-id' ; // Retrieved from workflow result
// Step 4: Mint a token
const mintResult = await client . tokenization . mintToken ( collectionId , {
addressId: process . env . ADDRESS_ID ,
clientShare: process . env . CLIENT_SHARE ,
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
metadata: {
name: 'My First NFT' ,
description: 'This is my first NFT' ,
image: 'https://example.com/image.png'
}
});
console . log ( 'Minting started. Workflow ID:' , mintResult . data . mint ?. workflowId );
// Step 5: Wait for mint to complete
let mintStatus ;
do {
await new Promise ( resolve => setTimeout ( resolve , 3000 ));
mintStatus = await client . temporal . getWorkflowStatus ({
workflowId: mintResult . data . mint ?. workflowId
});
console . log ( 'Mint status:' , mintStatus . data . status );
} while ( mintStatus . data . status === 'RUNNING' );
if ( mintStatus . data . status === 'COMPLETED' ) {
console . log ( 'Token minted successfully!' );
console . log ( 'Transaction Hash:' , mintStatus . data . result . transactionHash );
}
// Step 6: View tokens
const tokens = await client . tokenization . getCollectionTokens ({
id: collectionId ,
skip: 0 ,
take: 10
});
console . log ( 'Collection tokens:' , tokens . data . tokens );
} catch ( error ) {
console . error ( 'Error:' , error );
}
}
tokenizationWorkflow ();
Workflow Status Values
When checking workflow status, you’ll receive one of these values:
RUNNING : Operation is in progress
COMPLETED : Operation completed successfully
FAILED : Operation failed (check error field for details)
CANCELLED : Operation was cancelled
Common Issues
Collection Deployment Fails
Causes:
Insufficient gas balance
Invalid constructor parameters
Network connectivity issues
Solutions:
Ensure address has sufficient ETH for gas
Verify constructor parameters match contract requirements
Check network status and retry
Minting Fails
Causes:
Invalid token ID (not sequential for ERC-721)
Collection not deployed yet
Insufficient gas balance
Solutions:
Ensure token IDs are sequential starting from 0
Wait for collection deployment to complete
Verify sufficient gas balance
Workflow Status Stuck
Causes:
Network congestion
Transaction pending in mempool
Solutions:
Wait longer (some operations take time)
Check blockchain explorer for transaction status
Contact support if stuck for extended period
Best Practices
Always check workflow status before proceeding to next steps
Handle errors gracefully - workflows can fail
Use sequential token IDs for ERC-721 collections
Store workflow IDs for tracking and debugging
Implement retry logic for transient failures
Monitor gas prices for cost optimization
Validate addresses before minting tokens
Next Steps