Getting Started

Build embedded expense management into your product by integrating the Findity Expense API β€” including dynamic forms, tax logic, approval workflows, and expense reporting.

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:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Your Application            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  End-User    β”‚  Admin / Approver    β”‚
β”‚  Views       β”‚  Views               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚       Findity Expense API           β”‚
β”‚  (Fields Β· Expenses Β· Reports Β·     β”‚
β”‚   Approvals Β· Content Β· Cards)      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚       Findity Authentication        β”‚
β”‚  (OAuth 2.0 token management)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
πŸ’‘

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

  1. Complete the Getting started with the Findity API guide β€” this walks you through obtaining your client_id and client_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 a Bearer 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.

πŸ”‘

Always use authorization_code grant type when making requests on behalf of end users. The client_credentials grant gives you application-level access, which is appropriate for admin operations but won't return user-specific data from endpoints like /me.

β†’ 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:

GET /v1/expense/me?include=preferences,meta.capabilities

This returns the user's identity, preferences, and what they're permitted to do (e.g., canImpersonate, canViewAdmin).

List the user's organizations:

GET /v1/expense/me/organizations?include=settings,meta.permissions

Each organization includes a meta.permissions object that tells you exactly what the user can do within that organization:

PermissionDescription
canCreateExpenseUser can create new expenses
canCreateReportUser can create expense reports
canSendExpensesUser can submit individual expenses
canSendReportsUser can submit expense reports
canApproveUser is an approver in this organization
canAddCardUser can connect payment cards
πŸ’‘

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.

β†’ See the User and Organizations references for full details.


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:

  1. Fetch field definitions β€” the API returns a structured array of fields, each with a controlType, value, visible, mandatory, and validation state
  2. Render the fields dynamically β€” build your UI from the field definitions rather than hardcoding form layouts
  3. 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)
  4. 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:

Expense typeDescriptiontype value
Receipt expenseStandard purchases with receiptsReceiptVerification
MileageDistance-based travel claimsCarSpecification
Per diem / SubsistenceDaily allowances for business travelSubsistenceAllowance
Food benefitsMeal benefit reportingFoodBenefitsSpecification

Initialize fields for a chosen expense type:

GET /v1/expense/me/organizations/{id}/expensetypes/{expenseType}/fields

This returns a complete form definition for the chosen expense type, including:

  • 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 (url property on LIST fields)

Key field properties to handle

PropertyTypeWhat it means for your UI
controlTypestringHow to render: TEXT, DOUBLE, DATE, LIST, SWITCH, FIELD_GROUP, CONTENT_HOLDER, MILEAGE_ROUTE, INFORMATION, etc.
visiblebooleanOnly render fields where visible: true
mandatorybooleanMark as required in your UI
requiresUpdatebooleanWhen the user changes this field, call the PUT fields endpoint to get recalculated field definitions
disabledbooleanRender as read-only
valueanyCurrent field value β€” type depends on controlType
error / errorTextstringDisplay validation errors inline
urlstringFor LIST fields, fetch selectable options from this relative URL
dataobjectPre-loaded list item for the current value, so you don't need an extra API call

The update loop

When a user changes a field with requiresUpdate: true, you must send the full fields payload back to the API:

PUT /v1/expense/me/organizations/{id}/expensetypes/{expenseType}/fields

The API returns an updated fields definition β€” new fields may appear, others may hide, values may be recalculated, and validation errors may be added or cleared. This is how Findity's tax and business logic works without you implementing any of it.

⚠️

Always process the full response after a requiresUpdate round-trip. Don't assume only the changed field is affected β€” selecting a new category can change which custom dimensions are visible, which representation fields appear, or which tax rules apply.

πŸ’‘

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.

β†’ See the Fields reference for complete endpoint documentation.


Step 4: Build end-user views

End users need to create expenses, track their status, and manage expense reports. Use the processStatus filter to segment expenses into views.

Expense drafts

Draft expenses are expenses the user has created but not yet submitted.

GET /v1/expense/expenses?organizationId={orgId}&processStatus=DRAFT&include=meta.abbreviation,meta.capabilities

From this view, users should typically be able to:

  • 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 expenseRecordId query parameter, render the form, and update with PUT /v1/expense/expenses/{id}?format=fields
  • Attach receipts β€” upload images or PDFs via POST /v1/expense/content (with action=scan for automatic OCR), then reference the returned id in the verification.receiptAttachment field
  • 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:

GET /v1/expense/expenses?organizationId={orgId}&processStatus=PROCESSING&include=meta.abbreviation

This view is read-only for the end user. Show the status, amount, and category β€” the meta.abbreviation object provides pre-formatted display strings like reimbursementDescription and dateDescription.

Approved expenses

GET /v1/expense/expenses?organizationId={orgId}&processStatus=PROCESSED&include=meta.abbreviation

These are completed expenses that have been sent or paid. Display as a history view.

Declined expenses

GET /v1/expense/expenses?organizationId={orgId}&processStatus=REJECTED&include=meta.abbreviation

Rejected expenses can be edited and resubmitted. When loading the fields for a rejected expense, check for the 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

POST /v1/expense/expensereports?format=fields&organizationId={orgId}

Use the fields format for reports too β€” initialize report fields with:

GET /v1/expense/me/organizations/{id}/expensereports/fields

This returns fields for the report name, description, recipients, and any report-level custom dimensions.

Adding expenses to a report

You can add expenses to a report in two ways:

  1. At creation β€” include an array of expense record IDs in the report payload
  2. During editing β€” update the expenseRecords field in the report's fields payload

The expenseRecords field uses controlType: LIST with multiSelect: true. Use the filter canBeAddedToReport when listing expenses to show only eligible records:

GET /v1/expense/expenses?organizationId={orgId}&filter=canBeAddedToReport

Submitting a report

PUT /v1/expense/expensereports/{id}?action=send

Before sending, check the 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 same processStatus filter as expenses:

GET /v1/expense/expensereports?organizationId={orgId}&processStatus=DRAFT&include=meta.abbreviation,meta.capabilities,expenseRecords
πŸ’‘

Report preview: Each report includes a previewUrl in its metadata. Use this to offer users a PDF-style preview of the report before submission.

β†’ See the Expense Reports reference for full details.


Step 6: Build admin and approver views

Users with the canApprove permission in an organization can review, approve, or reject submitted expenses and reports.

Pending approvals

List all reports awaiting the current user's approval:

GET /v1/expense/me/organizations/{id}/approvals?include=meta.abbreviation,expenseRecords.meta.abbreviation

You can also group approvals by person for a cleaner overview:

GET /v1/expense/me/organizations/{id}/approvals/groups/person

This returns each person's name, profile picture, total amount, and number of pending approvals β€” ideal for a summary dashboard.

Reviewing individual expenses

To show the approver a detailed, read-only view of an expense:

GET /v1/expense/me/organizations/{id}/approvals/expense/{expenseId}/fields?include=meta.capabilities,meta.summary

This returns the full fields structure with all values filled in and 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:

PUT /v1/expense/me/organizations/{id}/approvals/{expenseReportId}

To approve the entire report:

{
  "approvalChainId": "the-approval-chain-id"
}

To reject specific expenses with comments:

{
  "rejectedExpenseRecords": [
    {
      "id": "expense-record-id",
      "rejectComment": "Receipt is missing β€” please re-upload"
    }
  ],
  "approvalChainId": "the-approval-chain-id"
}
πŸ’‘

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:

GET /v1/expense/expensereports?organizationId={orgId}&processStatus=PROCESSED&include=meta.abbreviation

β†’ See the Approvals reference for full details.


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.

Upload a receipt with OCR scanning:

curl -X POST https://stage-api.findity.com/api/v1/expense/content?action=scan&organizationId={orgId} \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: image/jpeg" \
  -H "Content-Name: receipt.jpg" \
  --data-binary @receipt.jpg

The response includes the content id (to reference in expense fields) and a scanResult with extracted data.

Retrieve receipt images at specific sizes:

GET /v1/expense/content/{id}/image?size=400x400

β†’ See the Content reference for full details.


Integration best practices

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

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

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

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

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

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

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

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

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.


Next steps

Dive into each area of the API for detailed endpoint documentation, request/response schemas, and examples: