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 afields array, optional meta (summary, capabilities), and optional validationErrors. Each field object describes one form element.
The field object
Control types
Map eachcontrolType to a UI component in your client:
Hidden and system fields
Many fields in the response havevisible: 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.
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
- Initialize — Call
GETon the fields endpoint to get the initial field structure. - User edits a field — If the edited field has
requiresUpdate: true, callPUTon the same endpoint with the full fields array (including the updated value). - 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.
- 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:- Iterate the
fieldsarray in order — The server controls field order. Render fields top-to-bottom as they appear. - Check
visible— Skip fields wherevisibleisfalse, but keep them in state. - Check
disabled— Render disabled fields as read-only. - Render by
controlType— Map eachcontrolTypeto your platform’s UI component. - Handle
FIELD_GROUPrecursively — AFIELD_GROUPcontains nestedfields. Render it as a section or expandable container, then render its children. - Show errors — If
erroris not null, displayerrorTextas inline validation feedback. - Show
INFORMATIONfields — Render these as banners or callouts styled byinformationType. - Load list data — For
LISTandLIST_SEARCHABLEfields, use thedataproperty for the initial display. When the user opens the picker, fetch the full list fromurl.
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:- Expenses
- Expense reports
- Cards
- User settings
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=fieldswith the fields payload.
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
erroranderrorTextpopulated. Re-render the form with these errors shown inline. - Top-level
validationErrors: An array of{errorText, errorCode, checkType}objects.checkTypecan beBLOCKER(cannot submit),ERROR(save issue), orWARNING(can still submit).
Recommended integration order
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.- Call
GET /me/organizations/{id}/expensetypes/ReceiptVerification/fieldsto get the field structure. - Build your generic field renderer that handles
TEXT,DOUBLE,DATE,LIST,SWITCH,CONTENT_HOLDER, andFIELD_GROUP. - Implement the
requiresUpdatecycle withPUT /me/organizations/{id}/expensetypes/ReceiptVerification/fields. - Save the expense with
POST /expenses?format=fields.
2. Mileage expenses (CarSpecification)
Adds theMILEAGE_ROUTE control type and the route travel group with commands.
- Call
GET /me/organizations/{id}/expensetypes/CarSpecification/fields. - Add
MILEAGE_ROUTErendering and command handling (clear travel route, round trip toggle). - Handle the
FIELD_GROUPfor travel route with its nested distance fields.
3. Per diem expenses (SubsistenceAllowance)
The most complex expense type. AddsLOCAL_DATE_TIME, dynamic travel stops (addStop/removeStop commands), and food/accommodation deductions.
- Call
GET /me/organizations/{id}/expensetypes/SubsistenceAllowance/fields. - Add
LOCAL_DATE_TIMErendering. - Implement add/remove stop commands (dynamic
FIELD_GROUPmanipulation). - Handle the food and accommodation deduction groups.
4. Food benefit expenses (FoodBenefitsSpecification)
A simpler expense type with breakfast/lunch/dinner toggles.- Call
GET /me/organizations/{id}/expensetypes/FoodBenefitsSpecification/fields. - No new control types needed — reuses
SWITCHandDATE.
5. Expense report fields
AddsRECIPIENT_LIST, URL (for preview), and multi-select LIST (for expense record selection).
- Call
GET /me/organizations/{id}/expensereports/fieldsto initialize. - Add
RECIPIENT_LISTandURLrendering. - Handle expense record list management.
6. Card fields
Read-only card details and editable card settings.- Call
GET /me/organizations/{id}/cards/{cardId}/fieldsfor read-only view. - Call
GET /me/organizations/{id}/cards/{cardId}/editfieldsfor editable view. - 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.- Call
GET /me/settings/fieldsto fetch settings. - Handle
COMMANDfields withclientCommand(edit email, edit phone, change password). - Handle temporary approver management (addTemporaryApprover/removeTemporaryApprover commands).
- Save with
PUT /me?format=fieldsfor persistent changes.
8. Approval fields
Read-only fields for the approval flow. All fields havedisabled: true.
- Call
GET /me/organizations/{id}/approvals/expense/{expenseId}/fieldsfor single expense approval. - Call
GET /me/organizations/{id}/approvals/expensereports/{reportId}/fieldsfor report approval. - Reuse your existing field renderer — the
disabledproperty handles the read-only state.

