State Machines
Learn about the state machines that manage the lifecycle of entities in the Bloque SDK.
Overview
The Bloque SDK uses well-defined state machines to manage the lifecycle of all major entities. Understanding these states and their transitions is essential for building robust integrations.
Main State Machines
π Accounts
Accounts manage user funds and follow a well-defined lifecycle from creation to deletion.
** Key states:**
creation_in_progress β active β disabled/frozen/deleted
- Handles errors:
creation_failed
Complete documentation: Virtual Accounts | Virtual Cards
πΈ Transfers
Transfers move funds between accounts and are processed asynchronously.
Key states:
queued β processing β completed/failed
Complete documentation: Transfers
π’ Organizations
Organizations represent legal entities (businesses or individuals) with compliance requirements.
Key states:
awaiting_compliance_verification β active β suspended/closed
Complete documentation: Organizations
π KYC/Compliance
Manages the identity verification and compliance process.
Key states:
awaiting_compliance_verification β approved/rejected
Complete documentation: Compliance & KYC
π Swap
Manages asset swap orders between different mediums and currencies.
Key states:
- Orders:
pending β in_progress β completed/failed
- Execution:
pending β running β completed/failed
Complete documentation: Swap
Common Patterns
π Transient vs Final States
Transient States:
creation_in_progress, processing, pending, awaiting_*
- Require action or time to change
- Usually have associated timeouts
Final States:
completed, failed, deleted, closed, approved, rejected
- Don't change without external intervention
- Represent the outcome of a process
β±οΈ Polling Pattern
For states that resolve asynchronously:
async function waitForCompletion(getStatus: () => Promise<{status: string}>) {
let attempts = 0;
const maxAttempts = 30; // 30 attempts
while (attempts < maxAttempts) {
const result = await getStatus();
if (['completed', 'failed', 'approved', 'rejected'].includes(result.status)) {
return result; // Final state reached
}
// Wait 2 seconds between attempts
await new Promise(resolve => setTimeout(resolve, 2000));
attempts++;
}
throw new Error('Timeout waiting for completion');
}
πͺ Webhook Pattern
For real-time notifications of state changes:
// Set webhook URL when creating entities
const account = await session.accounts.virtual.create({
firstName: 'John',
lastName: 'Doe',
webhookUrl: 'https://api.example.com/webhooks/account-status'
});
// Handle state changes
app.post('/webhooks/account-status', (req, res) => {
const { type, data } = req.body;
if (type === 'account.status_changed') {
console.log(`Account ${data.urn} changed to: ${data.status}`);
// Update local database
updateLocalAccount(data.urn, { status: data.status });
}
res.status(200).send('OK');
});
Error Handling by State
Common Error States
Recovery Strategies
async function handleFailedTransfer(sourceUrn: string, destinationUrn: string) {
// Verify accounts still exist and are active
const source = await bloque.accounts.get(sourceUrn);
const dest = await bloque.accounts.get(destinationUrn);
if (source.status !== 'active' || dest.status !== 'active') {
throw new Error('Source or destination account is not active.');
}
// Re-check available balance before retrying
const balances = await bloque.accounts.balance(sourceUrn);
if (!balances['DUSD/6'] || balances['DUSD/6'].current === '0') {
throw new Error('Insufficient funds. Check your balance.');
}
}
Best Practices
1. Check States Before Operations
// β
Good: verify state before transfer
const account = await session.accounts.get(accountUrn);
if (account.status !== 'active') {
throw new Error('Account must be active to transfer');
}
await session.accounts.transfer({
sourceUrn: accountUrn,
destinationUrn: destUrn,
amount: '1000000',
asset: 'DUSD/6'
});
2. Handle Transient States with Timeouts
async function waitForActive(accountUrn: string, timeoutMs = 30000) {
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
const account = await session.accounts.get(accountUrn);
if (account.status === 'active') {
return account;
}
if (account.status === 'creation_failed') {
throw new Error('Account creation failed');
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
throw new Error('Timeout waiting for account activation');
}
3. Implement Exponential Backoff Retries
async function retryOperation<T>(
operation: () => Promise<T>,
maxRetries = 3,
baseDelay = 1000
): Promise<T> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries) throw error;
const delay = baseDelay * Math.pow(2, attempt - 1);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
Quick References
State Summary
Flow Diagrams
For visualizing state transitions, check the specific entity guides where you'll find detailed transition tables and practical examples.
Next Steps