Integration overview
The Findity Expense API lets you embed full expense management functionality — receipt expenses, mileage claims, per diem / subsistence allowances, and food benefits — directly into your application. Instead of implementing complex tax rules and country-specific business logic yourself, you use Findity’s Fields system, which delivers dynamic form definitions that encapsulate all validation, tax calculations, and regulatory compliance. A typical integration involves these layers:Why Fields? The Fields system is the core of a Findity integration. Rather than hardcoding expense form structures, you fetch field definitions from the API and render them dynamically. This means your UI automatically adapts when organizations configure new categories, dimensions, or tax rules — with zero code changes on your side.
Prerequisites
- Complete the Getting started with the Findity API guide — this walks you through obtaining your
client_idandclient_secret, choosing your environment (stage or production), and making your first authenticated request.
Step 1: Authenticate as user
All API calls require a Bearer token. Obtain one using the OAuth 2.0 client credentials flow, then include it as aBearer token in the Authorization header of every request.
For end-user context (acting on behalf of a specific user), use the authorization grant flow to exchange a user ID for a user-scoped token.
→ See the Authentication guide for step-by-step instructions on obtaining API keys, getting tokens, refreshing tokens, and impersonating users. For endpoint details, see the Authentication reference.
Step 2: Establish user context
Once authenticated with a user token, fetch the current user’s profile and organizations. This gives you the foundation for all expense operations. Get the logged-in user:canImpersonate, canViewAdmin).
List the user’s organizations:
meta.permissions object that tells you exactly what the user can do within that organization:
Use these permissions to control which UI elements you show. For example, hide the “Approve” tab if
canApprove is false, or disable the “New expense” button if canCreateExpense is false.Step 3: Understand the Fields system
The Fields system is what sets a Findity integration apart from a raw REST API integration. Instead of constructing expense payloads manually, you:- Fetch field definitions — the API returns a structured array of fields, each with a
controlType,value,visible,mandatory, and validation state - Render the fields dynamically — build your UI from the field definitions rather than hardcoding form layouts
- Send field updates back — when a user changes a value on a field marked
requiresUpdate: true, send the entire fields payload back to get recalculated fields (e.g., selecting a country may reveal new tax-related fields) - Save using the fields format — submit the final fields payload to create or update the expense
Initializing fields for a new expense
The API supports four expense types, each with its own field structure:
Initialize fields for a chosen expense type:
- Standard fields — category, description, amount, currency, date, VAT, payment method
- Custom dimensions — organization-specific fields like cost center or project (
custom: true) - Nested field groups — complex structures like representation/entertainment details or travel routes
- List endpoints — URLs to fetch selectable values (
urlproperty on LIST fields)
Key field properties to handle
The update loop
When a user changes a field withrequiresUpdate: true, you must send the full fields payload back to the API:
Performance tip: Only trigger the update round-trip when the user modifies a field with
requiresUpdate: true. For all other fields, update the value locally and send the full payload only on save.Step 4: Build end-user views
End users need to create expenses, track their status, and manage expense reports. Use theprocessStatus filter to segment expenses into views.
Expense drafts
Draft expenses are expenses the user has created but not yet submitted.- Create new expenses — initialize fields for the chosen expense type, render the dynamic form, and save with
POST /v1/expense/expenses?format=fields - Edit existing drafts — load the expense fields with the
expenseRecordIdquery parameter, render the form, and update withPUT /v1/expense/expenses/{id}?format=fields - Attach receipts — upload images or PDFs via
POST /v1/expense/content(withaction=scanfor automatic OCR), then reference the returnedidin theverification.receiptAttachmentfield - Delete drafts —
DELETE /v1/expense/expenses/{id}
Receipt scanning: When you upload content with
action=scan, the response includes a scanResult with extracted data, e.g.amount, taxAmount, currency, purchaseDate, and suggested categoryIds. Apply these to the fields by passing the applyScanResult query parameter on the fields PUT endpoint.Submitted expenses
Once submitted, expenses move through the approval pipeline. List them with:meta.abbreviation object provides pre-formatted display strings like reimbursementDescription and dateDescription.
Approved expenses
Declined expenses
rejectionComment field (an INFORMATION type with informationType: ERROR) to display the approver’s reason for rejection.
Counters for badges: Use
GET /v1/expense/me/counters to fetch notification counts per organization — including expenses, approvals, rejections, and notifications — to show badge counts on your navigation tabs.Step 5: Handle expense reports
Most organizations require expenses to be grouped into reports before submission. The report workflow wraps multiple expenses into a single submission unit.Creating and managing reports
Adding expenses to a report
You can add expenses to a report in two ways:- At creation — include an array of expense record IDs in the report payload
- During editing — update the
expenseRecordsfield in the report’s fields payload
expenseRecords field uses controlType: LIST with multiSelect: true. Use the filter canBeAddedToReport when listing expenses to show only eligible records:
Submitting a report
meta.capabilities.canBeSentIn flag. If validation errors exist, the API returns them in the response so you can guide the user to fix issues.
Listing reports by status
Use the sameprocessStatus filter as expenses:
Report preview: Each report includes a
previewUrl in its metadata. Use this to offer users a PDF-style preview of the report before submission.Step 6: Build admin and approver views
Users with thecanApprove permission in an organization can review, approve, or reject submitted expenses and reports.
Pending approvals
List all reports awaiting the current user’s approval:Reviewing individual expenses
To show the approver a detailed, read-only view of an expense:disabled: true on every field — the approver can see all details but cannot edit them.
Approving or rejecting
Approve or reject individual expense records within a report:The
approvalChainId is returned on the expense report object. It identifies where in the approval chain the current approver sits — this is important for organizations with multi-level approval workflows.Approval history
Approved and processed reports are available through the standard report listing filtered by status:Step 7: Manage receipts and attachments
The Content API handles all file uploads — receipt images, PDF documents, and guest list attachments.Upload receipts before initializing the expense fields. That way, you can pass the
scanResult to pre-fill form fields, giving users a faster expense creation experience.bash
id (to reference in expense fields) and a scanResult with extracted data.
Retrieve receipt images at specific sizes:
Integration best practices
Cache list data appropriately
Cache list data appropriately
Fields with
controlType: LIST include a url property pointing to the list values endpoint. Cache these lists (categories, currencies, payment types) per organization and refresh periodically — they don’t change often, and caching reduces API calls significantly.Handle the fields update loop efficiently
Handle the fields update loop efficiently
Only trigger the fields update round-trip (
PUT on the fields endpoint) when the user changes a field marked requiresUpdate: true. For all other changes, store the value locally and send the complete payload only when saving. This minimizes API calls and keeps the UI responsive.Use meta.abbreviation for display
Use meta.abbreviation for display
The
meta.abbreviation object on expenses and reports provides pre-formatted, locale-aware strings for dates, amounts, and descriptions. Use these for list views instead of formatting raw values yourself — this ensures consistency with Findity’s formatting rules.Respect capabilities everywhere
Respect capabilities everywhere
Always check
meta.capabilities before rendering action buttons. For example, canBeSentIn tells you if an expense can be submitted directly, canBeSplit tells you if splitting is available, and canBeMerged indicates whether a card transaction expense can be merged with another expense. Disabling unavailable actions prevents confusing error responses.Test with the stage environment first
Test with the stage environment first
Use
https://stage-api.findity.com/api/ during development. The stage environment has the same API surface as production but lets you experiment freely without affecting real data.Handle validation errors gracefully
Handle validation errors gracefully
When using
format=fields, validation errors are returned inline with the fields structure. Check the error and errorText properties on each field to display validation feedback exactly where users need it, rather than showing generic error messages.Leverage OCR scan results
Leverage OCR scan results
Upload receipts with
action=scan before initializing expense fields. Pass the scanResult to the fields initialization to pre-fill form data, reducing manual data entry and improving user experience.Use include parameters strategically
Use include parameters strategically
Only request the data you need via
include parameters (e.g., include=meta.capabilities,meta.abbreviation). This reduces payload size and improves response times, especially for list endpoints.Implement proper error handling
Implement proper error handling
The API uses standard HTTP status codes and returns structured error responses with
code and description. Map common error codes (EXPENSE_RECORD_NOT_FOUND, INVALID_ORGANIZATION_ID, etc.) to user-friendly messages in your application.
