Documentation Index
Fetch the complete documentation index at: https://docs.finhub.cloud/llms.txt
Use this file to discover all available pages before exploring further.
Best Practices
Follow these best practices to ensure smooth integration with the FinHub API.
Registration Best Practices
✅ DO
| Practice | Description |
|---|
| Call categorization hierarchy first | Always retrieve available categories before registration |
| Build parametrization correctly | Match feature requirements exactly |
| Use strong passwords | Min 12 chars, mixed case, numbers, symbols |
| Store IDs securely | Save customer ID and user ID for future calls |
| Validate email format | Check email format before submission |
| Handle duplicates | Check for existing customers before creating new ones |
❌ DON’T
| Anti-Pattern | Issue |
|---|
| Hardcode category IDs | IDs may vary across environments |
| Skip categorization validation | Leads to registration failures |
| Use weak passwords | Will be rejected by validation |
| Expose credentials in logs | Security risk |
| Submit duplicate emails | Will cause conflicts |
| Ignore error responses | Miss important validation feedback |
Session Management Best Practices
Token Handling
// Good: Store token securely and check expiry
const tokenData = {
token: response.data.token,
expiresAt: Date.now() + (response.data.expiresIn * 1000),
refreshToken: response.data.refreshToken
};
function isTokenValid() {
return Date.now() < tokenData.expiresAt - 60000; // 1 min buffer
}
async function getValidToken() {
if (!isTokenValid()) {
await refreshToken();
}
return tokenData.token;
}
Session Security
- Never store tokens in localStorage for sensitive applications
- Use httpOnly cookies when possible
- Implement token refresh before expiry
- Clear tokens on logout from all storage
Verification Best Practices
✅ DO
| Practice | Description |
|---|
| Submit high-quality images | Clear, well-lit document photos |
| Check document expiry | Ensure documents are not expired |
| Provide all required documents | Check requiredDocuments array |
| Keep customer informed | Show verification status updates |
| Monitor progress | Poll status endpoint periodically |
| Handle rejection gracefully | Allow document resubmission |
❌ DON’T
| Anti-Pattern | Issue |
|---|
| Submit blurry images | Will be rejected |
| Use expired documents | Verification will fail |
| Skip required document types | Incomplete verification |
| Assume immediate approval | High-risk may take 1-3 days |
| Retry failed verification endlessly | Wait for guidance |
Document Quality Guidelines
| Requirement | Standard |
|---|
| Resolution | Min 300 DPI |
| Format | JPEG, PNG, or PDF |
| Size | Max 5 MB per document |
| Clarity | All text must be readable |
| Completeness | All corners visible |
Consent Management Best Practices
✅ DO
| Practice | Description |
|---|
| Accept all three consents | Terms, Privacy, Data Processing |
| Record acceptance metadata | IP, timestamp, user agent |
| Store consent versions | Track which version was accepted |
| Handle consent expiry | Re-prompt when consents expire |
Consent Acceptance Pattern
async function acceptAllConsents(customerId) {
const consents = ['terms', 'privacy', 'data-processing'];
const results = [];
for (const type of consents) {
const result = await acceptConsent(customerId, type, {
accepted: true,
version: '1.0',
acceptanceTimestamp: new Date().toISOString()
});
results.push(result);
}
return results;
}
Activation Best Practices
Pre-Activation Checklist
async function canActivate(customerId, tenantId) {
const checks = {
verification: await checkVerificationStatus(customerId),
termsConsent: await checkConsent(customerId, 'TERMS_AND_CONDITIONS'),
privacyConsent: await checkConsent(customerId, 'PRIVACY_POLICY'),
dataConsent: await checkConsent(customerId, 'DATA_PROCESSING')
};
const allPassed =
checks.verification === 'APPROVED' &&
checks.termsConsent === 'ACCEPTED' &&
checks.privacyConsent === 'ACCEPTED' &&
checks.dataConsent === 'ACCEPTED';
return {
canActivate: allPassed,
checks: checks,
missing: Object.entries(checks)
.filter(([_, v]) => v !== 'APPROVED' && v !== 'ACCEPTED')
.map(([k, _]) => k)
};
}
✅ DO
| Practice | Description |
|---|
| Verify all prerequisites | Check before attempting activation |
| Handle activation errors | Parse error response for missing items |
| Inform customer of success | Send confirmation notification |
| Store wallet details | Save IBAN and wallet ID |
❌ DON’T
| Anti-Pattern | Issue |
|---|
| Attempt activation without verification | Will fail with 422 |
| Skip consent acceptance | Will fail with 400 |
| Retry activation repeatedly | Don’t retry without fixing issues |
| Expose sensitive data in errors | Log internally only |
Transaction Best Practices
Order Lifecycle
✅ DO
| Practice | Description |
|---|
| Pre-register beneficiaries | Add beneficiaries before transfers |
| Use prepare/execute pattern | Always prepare before executing |
| Check limits before transfer | Verify against daily/monthly limits |
| Implement idempotency | Use unique references per transaction |
| Monitor order status | Track until COMPLETED or FAILED |
❌ DON’T
| Anti-Pattern | Issue |
|---|
| Execute without prepare | Missing fee calculation |
| Ignore order expiry | Prepared orders expire after 2-3 hours |
| Retry failed orders blindly | May cause duplicate transactions |
| Skip beneficiary validation | PEP/sanctions check required |
API Integration Patterns
Retry Strategy
async function apiCallWithRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.status === 429) {
// Rate limited - exponential backoff
await sleep(Math.pow(2, i) * 1000);
continue;
}
if (error.status >= 500) {
// Server error - retry with backoff
await sleep(Math.pow(2, i) * 1000);
continue;
}
// Client error (4xx) - don't retry
throw error;
}
}
throw new Error('Max retries exceeded');
}
Idempotency
// Generate unique reference for each transaction
function generateReference(prefix, customerId) {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 8);
return `${prefix}-${customerId.slice(-8)}-${timestamp}-${random}`;
}
// Example: TXN-550e8400-1705315200000-a1b2c3
Security Best Practices
| Area | Recommendation |
|---|
| API Keys | Store in environment variables, never in code |
| Tokens | Use short-lived access tokens with refresh |
| Passwords | Never log or store in plain text |
| PII Data | Encrypt at rest and in transit |
| Error Messages | Don’t expose internal details to users |
| Audit Logging | Log all API calls with sanitized data |
| Area | Recommendation |
|---|
| Batch Operations | Use bulk endpoints where available |
| Caching | Cache categorization hierarchy |
| Pagination | Use pagination for list endpoints |
| Async Processing | Don’t block on long operations |
| Connection Pooling | Reuse HTTP connections |