API Architecture Overview
Asset tokenization platforms require comprehensive APIs spanning token issuance, trading, custody, compliance, and investor management. These APIs bridge traditional financial systems with blockchain infrastructure, enabling applications, exchanges, custodians, and compliance providers to integrate tokenized assets seamlessly. Well-designed APIs abstract blockchain complexity while exposing necessary control and transparency.
API design must balance developer experience with security and compliance. RESTful APIs provide familiar interfaces for traditional fintech developers, while WebSocket connections enable real-time updates for trading and portfolio management. GraphQL endpoints offer flexible querying for complex data relationships. All APIs must implement robust authentication, authorization, rate limiting, and comprehensive audit logging for regulatory compliance.
Token Issuance APIs
Creating and Configuring Tokens
The issuance API enables authorized parties to create new tokenized assets, configure compliance rules, set economic parameters, and manage the token lifecycle. Issuance requires extensive validation of legal documentation, asset verification, and compliance setup before tokens can be minted and distributed to investors.
// TypeScript API interfaces for token issuance
interface TokenIssuanceAPI {
// Create new token offering
createOffering(request: CreateOfferingRequest): Promise<OfferingResponse>;
// Update offering details
updateOffering(
offeringId: string,
updates: Partial<CreateOfferingRequest>
): Promise<OfferingResponse>;
// Configure compliance rules
setComplianceRules(
offeringId: string,
rules: ComplianceRules
): Promise<void>;
// Upload legal documents
uploadDocument(
offeringId: string,
document: DocumentUpload
): Promise<{ documentId: string; ipfsHash: string }>;
// Mint tokens to investors
mintTokens(request: MintRequest): Promise<MintResponse>;
// Get offering status
getOffering(offeringId: string): Promise<OfferingResponse>;
// List all offerings
listOfferings(filters?: OfferingFilters): Promise<OfferingResponse[]>;
}
interface CreateOfferingRequest {
// Asset details
assetType: 'real-estate' | 'equity' | 'debt' | 'fund' | 'art' | 'commodity';
assetName: string;
assetDescription: string;
// Token economics
tokenSymbol: string;
totalSupply: string; // Use string for large numbers
pricePerToken: string;
currency: 'USD' | 'EUR' | 'GBP' | 'USDC' | 'USDT';
// Legal structure
legalEntity: {
name: string;
jurisdiction: string;
registrationNumber: string;
entityType: 'corporation' | 'llc' | 'trust' | 'spv';
};
// Offering terms
offeringType: 'public' | 'private';
regulatoryExemption?: 'RegD-506b' | 'RegD-506c' | 'RegA+' | 'RegCF' | 'RegS';
minimumInvestment: string;
maximumRaise: string;
offeringStartDate: string; // ISO 8601
offeringEndDate: string;
// Distribution terms
dividendFrequency?: 'monthly' | 'quarterly' | 'annual' | 'none';
lockupPeriodDays?: number;
// Blockchain configuration
blockchain: 'ethereum' | 'polygon' | 'avalanche' | 'binance-smart-chain';
tokenStandard: 'ERC-20' | 'ERC-721' | 'ERC-1400';
}
interface OfferingResponse extends CreateOfferingRequest {
offeringId: string;
tokenContractAddress?: string;
status: 'draft' | 'pending-approval' | 'active' | 'closed' | 'cancelled';
totalRaised: string;
investorCount: number;
createdAt: string;
updatedAt: string;
}
interface ComplianceRules {
// Investor requirements
requireKYC: boolean;
requireAccreditation: boolean;
allowedJurisdictions: string[];
blockedJurisdictions: string[];
// Transfer restrictions
transferLockupDays: number;
requireSecondaryApproval: boolean;
maximumHolders: number;
maximumHoldingPercentage: number;
// Reporting
reportingFrequency: 'monthly' | 'quarterly' | 'annual';
auditRequired: boolean;
}
interface DocumentUpload {
documentType: 'prospectus' | 'ppm' | 'subscription-agreement' |
'operating-agreement' | 'audit-report' | 'appraisal' | 'other';
fileName: string;
fileContent: Buffer;
description: string;
}
interface MintRequest {
offeringId: string;
investorAddress: string;
amount: string;
purchasePrice: string;
paymentReference: string;
kycVerificationId: string;
accreditationVerificationId?: string;
}
interface MintResponse {
transactionHash: string;
investorAddress: string;
amount: string;
status: 'pending' | 'confirmed' | 'failed';
blockNumber?: number;
timestamp: string;
}
interface OfferingFilters {
assetType?: string;
status?: string;
minTotalRaised?: string;
maxTotalRaised?: string;
}
// Example implementation
class TokenIssuanceService implements TokenIssuanceAPI {
constructor(
private apiKey: string,
private baseUrl: string = 'https://api.tokenization-platform.com'
) {}
async createOffering(
request: CreateOfferingRequest
): Promise<OfferingResponse> {
const response = await fetch(`${this.baseUrl}/v1/offerings`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify(request)
});
if (!response.ok) {
throw new Error(`Failed to create offering: ${response.statusText}`);
}
return await response.json();
}
async setComplianceRules(
offeringId: string,
rules: ComplianceRules
): Promise<void> {
const response = await fetch(
`${this.baseUrl}/v1/offerings/${offeringId}/compliance`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify(rules)
}
);
if (!response.ok) {
throw new Error(`Failed to set compliance rules: ${response.statusText}`);
}
}
async mintTokens(request: MintRequest): Promise<MintResponse> {
// Validate KYC before minting
await this.validateKYC(request.kycVerificationId);
if (request.accreditationVerificationId) {
await this.validateAccreditation(request.accreditationVerificationId);
}
const response = await fetch(
`${this.baseUrl}/v1/offerings/${request.offeringId}/mint`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify(request)
}
);
if (!response.ok) {
throw new Error(`Failed to mint tokens: ${response.statusText}`);
}
return await response.json();
}
async uploadDocument(
offeringId: string,
document: DocumentUpload
): Promise<{ documentId: string; ipfsHash: string }> {
const formData = new FormData();
formData.append('documentType', document.documentType);
formData.append('fileName', document.fileName);
formData.append('description', document.description);
formData.append('file', new Blob([document.fileContent]));
const response = await fetch(
`${this.baseUrl}/v1/offerings/${offeringId}/documents`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`
},
body: formData
}
);
if (!response.ok) {
throw new Error(`Failed to upload document: ${response.statusText}`);
}
return await response.json();
}
async getOffering(offeringId: string): Promise<OfferingResponse> {
const response = await fetch(
`${this.baseUrl}/v1/offerings/${offeringId}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to get offering: ${response.statusText}`);
}
return await response.json();
}
async listOfferings(filters?: OfferingFilters): Promise<OfferingResponse[]> {
const queryParams = new URLSearchParams(filters as any);
const response = await fetch(
`${this.baseUrl}/v1/offerings?${queryParams}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to list offerings: ${response.statusText}`);
}
return await response.json();
}
async updateOffering(
offeringId: string,
updates: Partial<CreateOfferingRequest>
): Promise<OfferingResponse> {
const response = await fetch(
`${this.baseUrl}/v1/offerings/${offeringId}`,
{
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify(updates)
}
);
if (!response.ok) {
throw new Error(`Failed to update offering: ${response.statusText}`);
}
return await response.json();
}
private async validateKYC(verificationId: string): Promise<void> {
// Implementation to verify KYC status
}
private async validateAccreditation(verificationId: string): Promise<void> {
// Implementation to verify accreditation
}
}
Trading APIs
Order Management and Execution
Trading APIs enable buying and selling of tokenized assets on secondary markets. These APIs must support order placement, order matching, trade execution, and settlement while enforcing compliance rules on every transaction. Integration with traditional exchanges and DeFi protocols requires standardized interfaces.
// TypeScript trading API interfaces
interface TradingAPI {
// Place order
placeOrder(order: OrderRequest): Promise<OrderResponse>;
// Cancel order
cancelOrder(orderId: string): Promise<void>;
// Get order status
getOrder(orderId: string): Promise<OrderResponse>;
// List orders
listOrders(filters?: OrderFilters): Promise<OrderResponse[]>;
// Get order book
getOrderBook(tokenAddress: string): Promise<OrderBook>;
// Execute trade
executeTrade(tradeRequest: TradeRequest): Promise<TradeResponse>;
// Get trade history
getTradeHistory(filters?: TradeFilters): Promise<TradeResponse[]>;
}
interface OrderRequest {
tokenAddress: string;
side: 'buy' | 'sell';
orderType: 'market' | 'limit' | 'stop' | 'stop-limit';
quantity: string;
price?: string; // Required for limit orders
stopPrice?: string; // Required for stop orders
timeInForce: 'GTC' | 'IOC' | 'FOK' | 'DAY';
expirationDate?: string;
}
interface OrderResponse extends OrderRequest {
orderId: string;
status: 'pending' | 'open' | 'partially-filled' | 'filled' | 'cancelled' | 'rejected';
filledQuantity: string;
remainingQuantity: string;
averagePrice: string;
totalValue: string;
fees: string;
createdAt: string;
updatedAt: string;
userId: string;
}
interface OrderFilters {
tokenAddress?: string;
status?: string;
side?: 'buy' | 'sell';
fromDate?: string;
toDate?: string;
}
interface OrderBook {
tokenAddress: string;
bids: OrderBookLevel[];
asks: OrderBookLevel[];
lastTradePrice: string;
timestamp: string;
}
interface OrderBookLevel {
price: string;
quantity: string;
orderCount: number;
}
interface TradeRequest {
buyOrderId: string;
sellOrderId: string;
quantity: string;
price: string;
}
interface TradeResponse {
tradeId: string;
tokenAddress: string;
buyOrderId: string;
sellOrderId: string;
buyer: string;
seller: string;
quantity: string;
price: string;
totalValue: string;
buyerFee: string;
sellerFee: string;
status: 'pending' | 'settling' | 'settled' | 'failed';
transactionHash?: string;
executedAt: string;
settledAt?: string;
}
interface TradeFilters {
tokenAddress?: string;
userId?: string;
fromDate?: string;
toDate?: string;
minValue?: string;
maxValue?: string;
}
// Trading service implementation
class TradingService implements TradingAPI {
constructor(
private apiKey: string,
private baseUrl: string = 'https://api.tokenization-platform.com'
) {}
async placeOrder(order: OrderRequest): Promise<OrderResponse> {
// Validate compliance before allowing order
await this.validateTradeCompliance(order);
const response = await fetch(`${this.baseUrl}/v1/orders`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify(order)
});
if (!response.ok) {
throw new Error(`Failed to place order: ${response.statusText}`);
}
return await response.json();
}
async getOrderBook(tokenAddress: string): Promise<OrderBook> {
const response = await fetch(
`${this.baseUrl}/v1/orderbook/${tokenAddress}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to get order book: ${response.statusText}`);
}
return await response.json();
}
async executeTrade(tradeRequest: TradeRequest): Promise<TradeResponse> {
const response = await fetch(`${this.baseUrl}/v1/trades`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify(tradeRequest)
});
if (!response.ok) {
throw new Error(`Failed to execute trade: ${response.statusText}`);
}
return await response.json();
}
async cancelOrder(orderId: string): Promise<void> {
const response = await fetch(
`${this.baseUrl}/v1/orders/${orderId}`,
{
method: 'DELETE',
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to cancel order: ${response.statusText}`);
}
}
async getOrder(orderId: string): Promise<OrderResponse> {
const response = await fetch(
`${this.baseUrl}/v1/orders/${orderId}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to get order: ${response.statusText}`);
}
return await response.json();
}
async listOrders(filters?: OrderFilters): Promise<OrderResponse[]> {
const queryParams = new URLSearchParams(filters as any);
const response = await fetch(
`${this.baseUrl}/v1/orders?${queryParams}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to list orders: ${response.statusText}`);
}
return await response.json();
}
async getTradeHistory(filters?: TradeFilters): Promise<TradeResponse[]> {
const queryParams = new URLSearchParams(filters as any);
const response = await fetch(
`${this.baseUrl}/v1/trades?${queryParams}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to get trade history: ${response.statusText}`);
}
return await response.json();
}
private async validateTradeCompliance(order: OrderRequest): Promise<void> {
// Check if user is KYC verified
// Check if user meets token-specific requirements
// Validate order doesn't violate holding limits
}
}
Portfolio Management APIs
Holdings and Performance Tracking
Portfolio APIs provide investors with comprehensive views of their tokenized asset holdings, performance metrics, transaction history, and income distributions. These APIs aggregate data across multiple tokens and blockchains, calculating returns, dividend yields, and asset allocations.
// TypeScript portfolio management API
interface PortfolioAPI {
// Get portfolio summary
getPortfolio(userId: string): Promise<Portfolio>;
// Get holdings
getHoldings(userId: string): Promise<Holding[]>;
// Get transaction history
getTransactions(
userId: string,
filters?: TransactionFilters
): Promise<Transaction[]>;
// Get dividend history
getDividends(userId: string): Promise<Dividend[]>;
// Get performance metrics
getPerformance(
userId: string,
period: 'day' | 'week' | 'month' | 'quarter' | 'year' | 'all'
): Promise<PerformanceMetrics>;
}
interface Portfolio {
userId: string;
totalValue: string;
totalCost: string;
totalGainLoss: string;
totalGainLossPercentage: number;
totalDividendsReceived: string;
assetAllocation: AssetAllocation[];
lastUpdated: string;
}
interface AssetAllocation {
assetType: string;
value: string;
percentage: number;
gainLoss: string;
gainLossPercentage: number;
}
interface Holding {
tokenAddress: string;
tokenSymbol: string;
tokenName: string;
assetType: string;
quantity: string;
averageCost: string;
currentPrice: string;
currentValue: string;
gainLoss: string;
gainLossPercentage: number;
dividendYield: number;
lastUpdated: string;
}
interface Transaction {
transactionId: string;
type: 'buy' | 'sell' | 'transfer-in' | 'transfer-out' | 'dividend';
tokenAddress: string;
tokenSymbol: string;
quantity: string;
price: string;
totalValue: string;
fees: string;
transactionHash: string;
timestamp: string;
}
interface TransactionFilters {
tokenAddress?: string;
type?: string;
fromDate?: string;
toDate?: string;
}
interface Dividend {
dividendId: string;
tokenAddress: string;
tokenSymbol: string;
amount: string;
currency: string;
exDate: string;
paymentDate: string;
transactionHash: string;
}
interface PerformanceMetrics {
period: string;
startValue: string;
endValue: string;
gainLoss: string;
gainLossPercentage: number;
dividendsReceived: string;
totalReturn: string;
totalReturnPercentage: number;
annualizedReturn: number;
}
API Security and Authentication
| Security Measure | Implementation | Purpose |
|---|---|---|
| API Keys | Unique keys per application/user | Basic authentication and tracking |
| OAuth 2.0 | Token-based authorization | Delegated access without sharing credentials |
| JWT Tokens | Signed JSON tokens with expiration | Stateless authentication with claims |
| Rate Limiting | Requests per minute/hour limits | Prevent abuse and ensure fair usage |
| IP Whitelisting | Restrict access to known IPs | Additional security for sensitive operations |
| Request Signing | HMAC signatures on requests | Verify request integrity and authenticity |
| TLS/HTTPS | Encrypted communications | Protect data in transit |
Key Takeaways
- Comprehensive APIs are essential for integrating tokenized assets with applications, exchanges, custodians, and compliance providers
- Issuance APIs manage the full token lifecycle from creation through compliance configuration to investor distribution
- Trading APIs enable secondary market transactions with order management, matching, execution, and settlement capabilities
- Portfolio APIs aggregate multi-token holdings and provide performance tracking, transaction history, and dividend reporting
- API security requires multiple layers including authentication, authorization, rate limiting, request signing, and comprehensive audit logging
- RESTful APIs provide familiar interfaces while WebSocket connections enable real-time trading and portfolio updates
- All API operations must enforce compliance rules and maintain detailed audit trails for regulatory reporting
Review Questions
- Describe the key components of a token issuance API request. What information must be provided to create a compliant offering?
- How do trading APIs enforce compliance rules on secondary market transactions? What checks occur before order placement?
- Explain the difference between market orders, limit orders, and stop orders in the trading API. When would each be used?
- What portfolio metrics are most important for investors in tokenized real estate? How do these differ from equity securities?
- Compare API key authentication with OAuth 2.0 for tokenization platform APIs. What are the security tradeoffs?
- Why is request signing (HMAC) important for sensitive API operations like token minting or large transfers?
- How should APIs handle blockchain reorganizations or transaction failures? What retry and confirmation strategies are appropriate?