Expense List API
Expense APIs
Expense List API
Retrieve a paginated list of expenses with filters by date, state, employee, amount, and tags
Expense List API
Authentication
All requests require an API key in the request headers. Headers:Request
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Number of records to return. Min 1, Max 100, Default 30. |
cursor | string | No | Cursor for pagination. Use the nextCursor value returned by the previous response. |
startDate | string | No | Start date for filtering (ISO 8601 UTC format: YYYY-MM-DDTHH:mm:ssZ). |
endDate | string | No | End date for filtering (ISO 8601 UTC format: YYYY-MM-DDTHH:mm:ssZ). |
state | array | No | Expense states to filter by. Allowed values: DRAFTED, PENDING, APPROVED, OUT_OF_POLICY. Currently supports exactly one state. |
employeeName | string | No | Filter by the name of the employee who raised the expense (partial match). |
tagId | integer | No | Filter expenses containing this organization tag id. |
minAmount | number | No | Filter expenses with amount greater than or equal to this value. |
maxAmount | number | No | Filter expenses with amount less than or equal to this value. |
Code Examples
Success Response
HTTP Status:200 OK
Response Fields:
| Field | Type | Description |
|---|---|---|
ok | boolean | Indicates whether the request was successful |
data | object | Contains the expense list response data |
data.expenses | array | List of expenses |
data.expenses[].id | string | Unique identifier (slug) for the expense |
data.expenses[].state | string | Current state of the expense. Possible values: DRAFTED, PENDING, APPROVED, OUT_OF_POLICY |
data.expenses[].syncState | string | Synchronization state with the accounting system (e.g., SYNCED, NOT_SYNCED) |
data.expenses[].expenseType | string | Type of the expense |
data.expenses[].paymentState | string | Payment state of the expense |
data.expenses[].currency | string | Currency code (e.g., INR) |
data.expenses[].totalAmount | number | Total amount of the expense |
data.expenses[].itemCount | integer | Number of line items in the expense |
data.expenses[].description | string | Description of the expense |
data.expenses[].identifier | string | Human-readable identifier / number for the expense |
data.expenses[].selfUrl | string | Shareable URL for viewing the expense in the web interface |
data.expenses[].transactionData | object | Transaction details for the expense |
data.expenses[].transactionData.utr | string | Unique Transaction Reference (UTR) of the payment |
data.expenses[].transactionData.dateConfirmed | string | Date the transaction was confirmed (falls back to expense date or creation date) |
data.expenses[].transactionData.narration | string | Bank narration for the transaction |
data.expenses[].transactionData.ledgerId | string or null | Ledger / instrument identifier the transaction is mapped to |
data.expenses[].transactionData.isSelfTransfer | boolean | Whether the transaction is a self-transfer |
data.expenses[].merchant | object | Merchant details |
data.expenses[].merchant.name | string or null | Merchant name |
data.expenses[].merchant.vpa | string or null | Merchant VPA (UPI address) |
data.expenses[].initiator | object | Details of the user who raised the expense |
data.expenses[].initiator.slug | string | Unique slug identifier of the initiator |
data.expenses[].initiator.name | string | Full name of the initiator |
data.expenses[].initiator.email | string | Email address of the initiator |
data.expenses[].initiator.id | string or null | Internal id of the initiator |
data.expenses[].cardInfo | object or null | Card details if the expense was paid by card. null otherwise |
data.expenses[].cardInfo.maskedCardNumber | string | Masked card number |
data.expenses[].cardInfo.description | string | Card description |
data.expenses[].cardInfo.name | string | Card name |
data.expenses[].syncStatus | string | Human-readable accounting sync status label |
data.expenses[].status | string | Human-readable status label for the expense |
data.totalAmountAndCountByCurrency | array | Aggregated total amount and count of matching expenses, grouped by currency |
data.totalAmountAndCountByCurrency[].currency | string | Currency code for the aggregate |
data.totalAmountAndCountByCurrency[].amount | number or string | Sum of expense amounts in this currency |
data.totalAmountAndCountByCurrency[].count | number or string | Number of expenses in this currency |
data.context | object | Pagination metadata |
data.context.count | number | Number of expenses returned in this page |
data.context.hasMore | boolean | Indicates whether there are more expenses to fetch |
data.context.nextCursor | string or null | Cursor to fetch the next page. null when there are no more results |
Expense State Values
| Value | Description |
|---|---|
DRAFTED | Expense is in draft state |
PENDING | Expense is pending approval |
APPROVED | Expense has been approved |
OUT_OF_POLICY | Expense was flagged as out of policy |
Response Example
Error Responses
Validation Error
HTTP Status:400 Bad Request
Access Denied
HTTP Status:403 Forbidden
Authentication Errors
HTTP Status:401 Unauthorized
Permission Errors
HTTP Status:403 Forbidden
Internal Error
HTTP Status:500 Internal Server Error
Best Practices
- Use
cursorandlimittogether to paginate; pass thenextCursorfrom the response as thecursorvalue for the next call startDateandendDateare filtered against the expense date — provide both for a bounded range- Use the
idfrom the response as the input to the Expense Details API for full expense information - The API returns at most 100 expenses per call regardless of the
limitvalue - Access is limited to users with expense read permission and an admin or bookkeeper role