Skip to main content
Use server-driven field definitions to dynamically render forms for expenses, expense reports, cards, and user settings. The Findity Expense API uses a server-driven UI pattern called Fields. Instead of hardcoding form layouts in your client, you fetch field definitions from the API and render forms dynamically. The server controls which fields to show, in what order, with what validation — and your client renders them.

Why use Fields

Building expense management forms is complex. Different organizations configure different categories, dimensions, tax rules, and approval flows. The Fields system handles this complexity on the server side so your client stays simple. Consistent behavior across platforms — Every client (iOS, Android, web) renders the same fields in the same order with the same validation. When the server adds a new field or changes visibility rules, all clients pick it up without a code change. Organization-specific configuration — Each organization can have different expense categories, custom dimensions, representation rules, and country-specific per diem logic. The Fields endpoints return exactly the right fields for the current user’s organization — you don’t need to implement this logic yourself. Reduced client-side complexity — Your client becomes a renderer. The server decides what to show, whether a field is mandatory, and when changing one field should trigger a re-fetch of the field structure. This eliminates hundreds of conditional branches in your client code.

How Fields work

Every Fields endpoint returns a JSON structure with a fields array, optional meta (summary, capabilities), and optional validationErrors. Each field object describes one form element.

The field object

Control types

Map each controlType to a UI component in your client:

Hidden and system fields

Many fields in the response have visible: false. These are system fields (like id, status, reportStatus) that carry state between the client and server. Include them in your PUT requests but do not render them in the UI.
Always send the complete fields array back to the server on PUT requests — including hidden fields and fields you didn’t modify. The server uses the full state to compute the response.

The update cycle

The Fields system uses a request-response cycle to keep the form state consistent. Some fields are interdependent — for example, changing the expense category can change which custom dimensions are visible, or changing a per diem destination triggers recalculation of deduction periods.

How requiresUpdate works

  1. Initialize — Call GET on the fields endpoint to get the initial field structure.
  2. User edits a field — If the edited field has requiresUpdate: true, call PUT on the same endpoint with the full fields array (including the updated value).
  3. Server returns updated fields — The response contains the new field structure. Replace your local state with this response. Fields may have changed visibility, added new options, or updated other values.
  4. Repeat — Continue until the user saves or submits.
Only call PUT when the user changes a field with requiresUpdate: true. For fields without this flag, update the value locally without making an API call.

Commands and actions

FIELD_GROUP and COMMAND fields can include a commands array. Each command has: To execute a command, send a PUT request with the command’s query parameters appended to the fields endpoint URL. Note: Some fields in user settings may include client-side action hints in the response, but these are not part of the formal Fields schema and should be handled as implementation-specific extensions.

Rendering fields

Follow these rules when building your field renderer:
  1. Iterate the fields array in order — The server controls field order. Render fields top-to-bottom as they appear.
  2. Check visible — Skip fields where visible is false, but keep them in state.
  3. Check disabled — Render disabled fields as read-only.
  4. Render by controlType — Map each controlType to your platform’s UI component.
  5. Handle FIELD_GROUP recursively — A FIELD_GROUP contains nested fields. Render it as a section or expandable container, then render its children.
  6. Show errors — If error is not null, display errorText as inline validation feedback.
  7. Show INFORMATION fields — Render these as banners or callouts styled by informationType.
  8. Load list data — For LIST and LIST_SEARCHABLE fields, use the data property for the initial display. When the user opens the picker, fetch the full list from url.

Handling summary on field groups

Some FIELD_GROUP fields include a summary array — a flat list of key-value pairs that summarize the group’s contents. Use this to display a compact preview when the group is collapsed (e.g., showing “Stockholm → Gothenburg, 468 km” for a mileage route).

Handling deletable field groups

In per diem travel routes, intermediate stops are represented as FIELD_GROUP fields with deletable: true and a deleteCommand. Render a delete action on these groups that executes the command via PUT.

Saving fields

When the user finishes editing:
Use the fields format for creating and updating expenses:
  • Create: POST /expenses?format=fields&organizationId={id}&expenseType={type} with the fields payload.
  • Update: PUT /expenses/{expenseId}?format=fields with the fields payload.
Alternatively, use the standard JSON format (POST /expenses or PUT /expenses/{id}) if you extract values from the fields into the standard expense object.

Handling validation errors

When a save fails due to validation, the response may contain:
  • Field-level errors: Individual fields will have error and errorText populated. Re-render the form with these errors shown inline.
  • Top-level validationErrors: An array of {errorText, errorCode, checkType} objects. checkType can be BLOCKER (cannot submit), ERROR (save issue), or WARNING (can still submit).
Integrate the Fields endpoints in this order, starting with the simplest case and building up:

1. Receipt expenses (ReceiptVerification)

Start here — this is the most common expense type and has the most straightforward field structure.
  1. Call GET /me/organizations/{id}/expensetypes/ReceiptVerification/fields to get the field structure.
  2. Build your generic field renderer that handles TEXT, DOUBLE, DATE, LIST, SWITCH, CONTENT_HOLDER, and FIELD_GROUP.
  3. Implement the requiresUpdate cycle with PUT /me/organizations/{id}/expensetypes/ReceiptVerification/fields.
  4. Save the expense with POST /expenses?format=fields.

2. Mileage expenses (CarSpecification)

Adds the MILEAGE_ROUTE control type and the route travel group with commands.
  1. Call GET /me/organizations/{id}/expensetypes/CarSpecification/fields.
  2. Add MILEAGE_ROUTE rendering and command handling (clear travel route, round trip toggle).
  3. Handle the FIELD_GROUP for travel route with its nested distance fields.

3. Per diem expenses (SubsistenceAllowance)

The most complex expense type. Adds LOCAL_DATE_TIME, dynamic travel stops (addStop/removeStop commands), and food/accommodation deductions.
  1. Call GET /me/organizations/{id}/expensetypes/SubsistenceAllowance/fields.
  2. Add LOCAL_DATE_TIME rendering.
  3. Implement add/remove stop commands (dynamic FIELD_GROUP manipulation).
  4. Handle the food and accommodation deduction groups.

4. Food benefit expenses (FoodBenefitsSpecification)

A simpler expense type with breakfast/lunch/dinner toggles.
  1. Call GET /me/organizations/{id}/expensetypes/FoodBenefitsSpecification/fields.
  2. No new control types needed — reuses SWITCH and DATE.

5. Expense report fields

Adds RECIPIENT_LIST, URL (for preview), and multi-select LIST (for expense record selection).
  1. Call GET /me/organizations/{id}/expensereports/fields to initialize.
  2. Add RECIPIENT_LIST and URL rendering.
  3. Handle expense record list management.

6. Card fields

Read-only card details and editable card settings.
  1. Call GET /me/organizations/{id}/cards/{cardId}/fields for read-only view.
  2. Call GET /me/organizations/{id}/cards/{cardId}/editfields for editable view.
  3. Save changes with PUT /me/organizations/{id}/cards/{cardId}/editfields.

7. User settings fields

Personal settings, bank details, experience settings, app settings, notification settings, and temporary approvers.
  1. Call GET /me/settings/fields to fetch settings.
  2. Handle COMMAND fields with clientCommand (edit email, edit phone, change password).
  3. Handle temporary approver management (addTemporaryApprover/removeTemporaryApprover commands).
  4. Save with PUT /me?format=fields for persistent changes.

8. Approval fields

Read-only fields for the approval flow. All fields have disabled: true.
  1. Call GET /me/organizations/{id}/approvals/expense/{expenseId}/fields for single expense approval.
  2. Call GET /me/organizations/{id}/approvals/expensereports/{reportId}/fields for report approval.
  3. Reuse your existing field renderer — the disabled property handles the read-only state.

Fields endpoints reference

Last modified on August 31, 2026