API Philosophy
New Expensify’s API is designed around these core principles:- 1:1:1 Ratio: One user action → One API command → One database operation
- Optimistic First: UI updates before server confirmation
- Offline Support: All requests work offline and sync later
- No Client Logic: API responses contain data, not instructions
- Pusher Integration: Real-time updates via WebSocket
For complete API details, see contributingGuides/API.md in the repository.
API Structure
Location
API code is organized insrc/libs/API/:
Command Types
All API commands are defined insrc/libs/API/types.ts:
Making API Calls
API.write() - WRITE Requests
For actions that modify data. Requests are queued and retried if needed.- Creating new data
- Updating existing data
- Deleting data
- Any action that should work offline
API.read() - READ Requests
For fetching data without modifying server state.- Loading initial data
- Refreshing data
- Data that’s not persisted
- Commands that don’t need offline support
API.makeRequestWithSideEffects() - Side Effect Requests
For requests that need the response data immediately.- Third-party integration (Plaid, Onfido)
- Redirects based on response
- Cannot proceed without server data
Response Handling
HTTPS Response
Data returned directly to the requesting client:- READ responses
- Client-specific data
- Error messages
Pusher Events
Data broadcast to all connected clients:- WRITE responses (when successful)
- Real-time updates
- Multi-user data sync
Command Naming Conventions
API commands follow strict naming rules:Format
RequestMoneyOpenReportAcceptTaskSignIn
Rules
- Unique verbs: Use descriptive verbs (Request, Open, Accept)
- Standard verbs only if unique fails: Update, Add, Delete
- No parameter names: Bad:
SignInWithPassword, Good:SignIn - No implementation details: Bad:
AddVBBA, Good:AddBankAccount - No UI terms: Bad:
OpenReportPage, Good:OpenReport - One command per action: Not
StartAppWhileSignedInAndOpenWorkspace
Examples
Onyx Data Updates
Update Types
Three types of Onyx updates in API calls:When to Include Each Data Type
Example: Complete Onyx Updates
Error Handling
Error Structure
Errors are stored with timestamps:Displaying Errors
UseOfflineWithFeedback component:
Middleware
API requests pass through middleware insrc/libs/Middleware/:
- Logging: Log request details
- RecheckConnection: Verify connectivity
- Reauthentication: Handle expired auth (jsonCode 407)
- HandleDeletedAccount: Handle account deletion (jsonCode 408)
- SupportalPermission: Handle permission denials
- HandleUnusedOptimisticID: Update IDs in queued requests
- Pagination: Handle paginated responses
- SentryServerTiming: Track server performance
- SaveResponseInOnyx: Apply success/failure data
- FraudMonitoring: Tag requests for fraud detection
Pusher Integration
Real-time updates via Pusher WebSocket:Pusher Socket ID
WRITE requests include pusherSocketID to prevent echo:Best Practices
1. One API Call Per User Action
2. Always Provide Failure Data
3. Use Correct Request Type
Next Steps
Authentication
Learn about API authentication
Endpoints
Explore available API endpoints
State Management
Understand Onyx integration
Offline-First
Learn offline patterns
