Prompt
How do I set up an account opening API for wallet and ledger workflows in a fintech app?
Latest observation
To set up an account opening API for wallet + ledger workflows in a fintech app, think of it as two layers:
- Customer/account onboarding layer — creates the user-facing wallet account
- Ledger layer — creates the financial accounting records that back the wallet
A good design keeps them separate but linked by a shared account_id or wallet_id.
1) Define the core entities
You’ll usually need these objects:
- Customer: the person/business using the app
- Wallet: the product-facing account shown in the app
- Ledger account(s): internal accounting accounts for assets, liabilities, fees, etc.
- KYC/KYB profile: identity and compliance data
- Funding instrument: bank account, card, etc. linked later
- Account status: pending, active, suspended, closed
Common relationship
customer→ can have one or morewalletswallet→ maps to one or moreledger_accounts- transactions → post to the ledger, not directly to the wallet balance
2) Use a workflow-based API
Account opening is rarely a single call. It’s better as a state machine.
Suggested statuses
draftpending_verificationpending_approvalactiverejectedclosed
Typical flow
- Create customer
- Submit identity/business info
- Run KYC/KYB checks
- Create wallet account
- Create ledger accounts
- Activate wallet only after ledger setup succeeds
3) Suggested API endpoints
Customer creation
POST /v1/customers
Example body:
{
"type": "individual",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"country": "US",
"date_of_birth": "1990-02-01"
}
Response:
{
"customer_id": "cus_123",
"status": "created"
}
Start account opening
POST /v1/accounts
Example body:
{
"customer_id": "cus_123",
"account_type": "wallet",
"currency": "USD",
"product_code": "standard_wallet"
}
Response:
{
"account_id": "acc_456",
"status": "pending_verification"
}
Submit verification details
POST /v1/accounts/{account_id}/verification
Example body:
{
"identity_documents": [
{
"type": "passport",
"document_id": "doc_789"
}
],
"address": {
"line1": "123 Main St",
"city": "Austin",
"state": "TX",
"postal_code": "78701",
"country": "US"
}
}
Create ledger setup
POST /v1/accounts/{account_id}/ledger
This should create:
- wallet balance account
- settlement/clearing accounts
- fee accounts if needed
Example response:
{
"account_id": "acc_456",
"ledger_accounts": [
{
"ledger_account_id": "led_001",
"type": "liability",
"currency": "USD"
}
],
"status": "active"
}
4) Model the ledger properly
A wallet balance should be derived from ledger postings, not stored as the source of truth.
Double-entry example
If a user adds $100:
- Debit: Cash/settlement asset +100
- Credit: Customer wallet liability +100
This ensures:
- every transaction balances
- auditability is preserved
- reversals and chargebacks are possible
Ledger tables you may need
accountsjournal_entriesjournal_linesbalances(derived/cache, not authoritative)transfersholdsreversals
5) Make the API idempotent
Account opening often gets retried. Use idempotency keys.
Example:
POST /v1/accounts
Idempotency-Key: 8f1b2d...
This prevents duplicate wallet or ledger creation.
6) Handle asynchronous compliance checks
KYC/KYB and sanctions checks often take time.
Recommended pattern
- API returns
pending_verification - webhook notifies when review completes
- app polls status if needed
Example webhook:
{
"event_type": "account.verification.completed",
"account_id": "acc_456",
"result": "approved"
}
Then:
- create ledger account
- activate wallet
- allow funding/spending
7) Define the open-account orchestration service
A dedicated orchestration service is helpful.
Responsibilities
- validate input
- create customer
- call KYC provider
- create wallet record
- create ledger records
- activate account
- emit events
Example internal sequence
CustomerService.create()ComplianceService.submit()WalletService.create()LedgerService.create_accounts()AccountService.activate()
Use sagas or transactions with compensation to handle partial failures.
8) Ensure strong consistency where needed
For wallet/ledger workflows:
- Ledger posting must be atomic
- Balance updates should be derived from ledger
- Use database transactions for journal entry + line inserts
- Consider locking or optimistic concurrency for balance reads
Avoid:
- updating wallet balance independently from the ledger
- creating wallet without ledger mapping
- allowing payment actions before ledger activation
9) Security and compliance basics
You’ll want:
- OAuth2/JWT or mTLS for auth
- request signing if partners call the API
- PII encryption at rest
- field-level masking
- audit logs for every change
- RBAC for internal tools
- sanctions/AML screening integration
- document storage with secure object storage
10) Example minimal account opening flow
Step 1: Create customer
POST /v1/customers
Step 2: Open wallet
POST /v1/accounts
Step 3: Compliance review completes
Webhook or manual approval
Step 4: Create ledger accounts
POST /v1/accounts/{id}/ledger
Step 5: Activate
POST /v1/accounts/{id}/activate
11) Suggested response model
A good account-opening response often includes:
{
"account_id": "acc_456",
"customer_id": "cus_123",
"status": "pending_verification",
"wallet": {
"wallet_id": "wal_001",
"currency": "USD"
},
"ledger": {
"setup_status": "not_created"
},
"next_actions": [
"submit_verification",
"await_review"
]
}
12) Best practices summary
- Separate wallet from ledger
- Use double-entry accounting
- Make operations idempotent
- Prefer state machine workflows
- Support async compliance
- Use webhooks/events
- Keep ledger atomic and authoritative
- Design for auditability and reversals
If you want, I can also provide:
- a REST API spec for account opening,
- a database schema for wallet/ledger,
- or a sample sequence diagram for the full onboarding flow.