Fields - Receipt expenses
Create and manage receipt-based expenses using the dynamic Fields system in the Expense API.
Create receipt-based expenses using the dynamic Fields system. This guide covers uploading receipts with OCR, initializing fields, handling categories and tax, managing representation expenses, and saving.
Overview
Receipt expenses use the ReceiptVerification expense type. They represent standard purchases where a user submits a receipt for reimbursement — covering everything from office supplies and meals to travel costs and representation/entertainment.
This is the most common expense type and has the most straightforward field structure, making it the recommended starting point for your Fields integration. The server handles tax calculations, VAT logic, and country-specific rules — your client renders the fields and lets the user fill them in.
Key concepts
| Concept | Description |
|---|---|
| OCR scanning | Upload a receipt image before creating the expense. The API extracts amount, tax, currency, date, and suggests categories — pre-filling the form. |
| Categories | Each organization defines expense categories (e.g., Travel, Meals, Office supplies). Changing the category can alter which fields are visible. |
| Tax / VAT | Tax fields are calculated server-side based on category, country, and amount. Your client displays them but doesn't compute them. |
| Representation | Entertainment/representation expenses have additional fields: guest lists, purpose, and representation type. These appear dynamically when the category requires it. |
| Content | Receipt images and PDF attachments are uploaded separately via the Content API and referenced by ID in the fields. |
API endpoints
| Operation | Method | Endpoint |
|---|---|---|
| Upload receipt (with OCR) | POST | /v1/expense/content?action=scan&organizationId={id} |
| Upload receipt (without OCR) | POST | /v1/expense/content?organizationId={id} |
| Initialize fields | GET | /v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/fields |
| Update fields | PUT | /v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/fields |
| Create expense | POST | /v1/expense/expenses?format=fields&organizationId={id}&expenseType=ReceiptVerification |
| Update expense | PUT | /v1/expense/expenses/{expenseId}?format=fields |
| Get categories | GET | /v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/categories |
| Get currencies | GET | /v1/expense/me/organizations/{id}/currencies |
| Get payment types | GET | /v1/expense/me/organizations/{id}/paymenttypes |
| Retrieve receipt image | GET | /v1/expense/content/{id}/image?size={width}x{height} |
Step 1: Upload the receipt
Upload the receipt image before initializing fields. Use action=scan to trigger OCR, which extracts data that can pre-fill the expense form.
curl -X POST "https://stage-api.findity.com/api/v1/expense/content?action=scan&organizationId={orgId}" \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: image/jpeg" \
-H "Content-Name: receipt.jpg" \
--data-binary @receipt.jpgExample response
{
"id": "ff808181826e28b201826e56fc980005",
"name": "receipt.jpg",
"contentType": "image/jpeg",
"scanResult": {
"id": "ff808181826e28b201826e5700e30006",
"amount": 350.00,
"taxAmount": 70.00,
"currency": "SEK",
"purchaseDate": "2024-03-15T00:00:00Z",
"categoryIds": [
"a1b2c3d4e5f6",
"b2c3d4e5f6a1"
],
"categoryId": "a1b2c3d4e5f6"
}
}Scan result fields
| Field | Type | Description |
|---|---|---|
id | string | Scan result ID — pass to fields initialization and category lookup |
amount | number | Extracted total amount |
taxAmount | number | Extracted tax/VAT amount |
currency | string | Detected currency code |
purchaseDate | string | Detected purchase date (ISO 8601) |
categoryIds | string[] | Up to 3 suggested category IDs, ordered by confidence |
categoryId | string | Top suggested category ID (convenience shorthand for categoryIds[0]) |
Not all fields are guaranteed in a scan result — OCR accuracy depends on receipt quality. Always let the user review and correct pre-filled values.
Category suggestions from scan
Pass the scanResultId when fetching categories to receive 0–3 suggested categories:
GET /v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/categories?scanResultId={scanResultId}
Suggested categories are returned with a suggested: true flag. Display them prominently in your category picker.
Step 2: Initialize fields
Fetch the field structure for a new receipt expense:
curl -X GET "https://stage-api.findity.com/api/v1/expense/me/organizations/{orgId}/expensetypes/ReceiptVerification/fields" \
-H "Authorization: Bearer {access_token}"To apply scan results during initialization, pass the applyScanResult parameter:
curl -X GET "https://stage-api.findity.com/api/v1/expense/me/organizations/{orgId}/expensetypes/ReceiptVerification/fields?applyScanResult={scanResultId}" \
-H "Authorization: Bearer {access_token}"To load an existing expense for editing:
curl -X GET "https://stage-api.findity.com/api/v1/expense/me/organizations/{orgId}/expensetypes/ReceiptVerification/fields?expenseRecordId={expenseId}" \
-H "Authorization: Bearer {access_token}"Field structure overview
The response contains these key fields:
| Field property | controlType | Description |
|---|---|---|
id | TEXT | Expense ID (hidden, system field) |
categoryId | LIST | Expense category — requiresUpdate: true |
verification.comment | MULTI_LINE_TEXT | User comment |
verification.description | TEXT | Expense description |
expenseReportId | LIST | Assign to an expense report |
verification.receiptAttachment | CONTENT_HOLDER | Receipt image or PDF attachment |
verification.purchaseDate | DATE | Date of purchase — requiresUpdate: true |
verification.currencyCode | LIST | Transaction currency — requiresUpdate: true |
verification.totalAmount | DOUBLE | Total amount including tax (shown as "Amount incl. VAT") |
verification.taxAmount | DOUBLE | Tax / VAT amount |
verification.paymentTypeId | LIST | Payment method (cash, corporate card, etc.) |
verification.verificationNumber | TEXT | System-generated verification number (read-only) |
verification.storeName | TEXT | Store or merchant name |
verification.exchangeRate | DOUBLE | Exchange rate (appears when foreign currency selected) |
| Custom dimension fields | LIST | Organization-specific dimensions (e.g., cost center, project) |
Step 3: Handle categories and tax
The category field (categoryId) has requiresUpdate: true. Changing the category triggers a server round-trip that can:
- Show or hide tax fields based on the category's tax rules
- Reveal representation fields (guest list, purpose) for entertainment categories
- Change which custom dimensions are visible
- Update VAT calculation logic
Fetching categories
GET /v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/categories
Example response
{
"meta": { "count": 5, "total": 5 },
"data": [
{
"id": "a1b2c3d4e5f6",
"name": "Travel",
"iconUrl": "https://expense.findity.com/api/resources/...",
"section": "Common"
},
{
"id": "b2c3d4e5f6a1",
"name": "Meals & Entertainment",
"iconUrl": "https://expense.findity.com/api/resources/...",
"section": "Representation"
}
]
}Categories may include a section property for grouping in the UI.
Tax fields
Tax-related fields appear based on the selected category and country. Common tax fields include:
| Field property | controlType | Description |
|---|---|---|
verification.taxAmount | DOUBLE | Tax / VAT amount — may be auto-calculated |
verification.nonDeductibleVat | DOUBLE | Non-deductible VAT portion |
verification.taxPercentage | LIST | Tax rate selector (where applicable) |
Tax fields are computed server-side. When the user changes
totalAmount,currencyCode, orcategoryId, send a PUT to get recalculated tax values. Never compute tax client-side — rules vary by country, category, and organization settings.
Step 4: Handle currency and exchange rates
When a user selects a foreign currency (different from the organization's reimbursement currency), additional exchange rate fields appear:
| Field property | controlType | Description |
|---|---|---|
verification.currencyCode | LIST | Transaction currency — requiresUpdate: true |
verification.exchangeRate | DOUBLE | Exchange rate to reimbursement currency |
The currencyCode field has requiresUpdate: true — changing it triggers a server round-trip that fetches the current exchange rate. The reimbursement amount is calculated at the expense record level (not within the verification object) and returned as reimbursementAmount on the expense itself.
Fetching currencies
GET /v1/expense/me/organizations/{id}/currencies
Returns the list of currencies available in the organization. The data property on the currencyCode field contains the currently selected currency.
Step 5: Handle representation expenses
When a category requires representation/entertainment details, additional fields appear dynamically after the category is selected:
| Field property | controlType | Description |
|---|---|---|
verification.representation.type | LIST | Type of representation (internal, external, etc.) — requiresUpdate: true |
verification.representation.guests | GUEST_LIST | List of guests with name and company |
verification.representation.numberOfGuests | INTEGER | Total number of guests |
verification.representation.numberOfExternalGuests | INTEGER | Number of external guests |
verification.representation.place | TEXT | Place or venue name |
verification.representation.includesLiquor | SWITCH | Whether liquor/wine/strong beer was included |
verification.representation.guestListAttached | SWITCH | Whether to attach a guest list file — requiresUpdate: true |
verification.representation.guestListAttachment | CONTENT_HOLDER | Attached guest list PDF or document |
Guest list format
The GUEST_LIST control type holds an array of guest objects:
[
{ "name": "Jane Smith", "company": "Acme Corp" },
{ "name": "John Doe", "company": "Example Ltd" }
]Representation types
Fetch representation types for the selected category:
GET /v1/expense/me/organizations/{id}/representationtypes
The verification.representation.type field has requiresUpdate: true — changing it may alter visibility of other representation fields based on the organization's configuration.
Step 6: The update cycle
Receipt expenses have several fields with requiresUpdate: true. When the user changes any of these, send the full fields array back to the server:
PUT /v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/fields
Fields that trigger an update:
| Field | What changes on update |
|---|---|
categoryId | Tax rules, visible dimensions, representation fields, VAT logic |
verification.currencyCode | Exchange rate, reimbursement amount |
verification.totalAmount | Tax recalculation (in some configurations) |
verification.representation.type | Representation field visibility |
verification.paymentTypeId | Reimbursement-related fields |
Example update request
curl -X PUT "https://stage-api.findity.com/api/v1/expense/me/organizations/{orgId}/expensetypes/ReceiptVerification/fields" \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"fields": [
{ "property": "id", "value": null },
{ "property": "categoryId", "value": "a1b2c3d4e5f6" },
{ "property": "verification.receiptAttachment", "value": "ff808181826e28b201826e56fc980005" },
{ "property": "verification.purchaseDate", "value": "2024-03-15T00:00:00Z" },
{ "property": "verification.currencyCode", "value": "SEK" },
{ "property": "verification.totalAmount", "value": 350.00 },
{ "property": "verification.taxAmount", "value": 70.00 },
{ "property": "verification.paymentTypeId", "value": "CASH" },
{ "property": "verification.comment", "value": "Office supplies for Q1 planning" }
]
}'Always send the complete fields array — including hidden fields and fields you didn't modify. The server uses the full state to compute the response.
Step 7: Save the expense
Once the user has filled in all required fields, save the receipt expense.
Using fields format (recommended)
Create:
POST /v1/expense/expenses?format=fields&organizationId={id}&expenseType=ReceiptVerification
Update:
PUT /v1/expense/expenses/{expenseId}?format=fields
Send the full fields payload as the request body in both cases.
Using standard JSON format
Alternatively, extract values from the fields and use the standard expense format:
curl -X POST "https://stage-api.findity.com/api/v1/expense/expenses" \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"organizationId": "ff808181963979e20196397a2098004b",
"categoryId": "a1b2c3d4e5f6",
"verification": {
"type": "ReceiptVerification",
"purchaseDate": "2024-03-15T00:00:00Z",
"amount": 350.00,
"taxAmount": 70.00,
"currency": "SEK",
"paymentTypeId": "a7384ac0d3dc45bf9d15f8de343cf612",
"comment": "Office supplies for Q1 planning",
"receiptAttachment": {
"id": "ff808181826e28b201826e56fc980005"
}
}
}'
``
### Example response
```json
{
"id": "8a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d",
"organizationId": "ff808181963979e20196397a2098004b",
"personId": "ff808181963979e20196397a119f002d",
"status": "NORMAL",
"processStatus": "DRAFT",
"categoryId": "a1b2c3d4e5f6",
"reimbursementCurrency": "SEK",
"reimbursementAmount": 350.00,
"dateCreated": "2024-03-15T10:30:00Z",
"lastUpdated": "2024-03-15T10:30:00Z",
"verification": {
"type": "ReceiptVerification",
"purchaseDate": "2024-03-15T00:00:00Z",
"amount": 350.00,
"taxAmount": 70.00,
"currency": "SEK",
"paymentTypeId": "a7384ac0d3dc45bf9d15f8de343cf612",
"comment": "Office supplies for Q1 planning",
"receiptAttachment": {
"id": "ff808181826e28b201826e56fc980005"
}
},
"meta": {
"abbreviation": {
"reimbursementDescription": "350.00 SEK",
"dateDescription": "15 Mar 2024"
},
"capabilities": {
"canBeEdited": true,
"canBeDeleted": true,
"canBeSentIn": true,
"canBeMerged": false
}
}
}Receipt attachments
The verification.receiptAttachment field uses the CONTENT_HOLDER control type. It stores the content ID from the upload step.
Uploading additional attachments
Some expenses support multiple attachments. Upload each file separately and reference the IDs:
curl -X POST "https://stage-api.findity.com/api/v1/expense/content?organizationId={orgId}" \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/pdf" \
-H "Content-Name: invoice.pdf" \
--data-binary @invoice.pdfRetrieving receipt images
Fetch receipt images at a specific size for display in your UI:
GET /v1/expense/content/{id}/image?size=400x400
The API resizes the image to fit within the specified dimensions while preserving aspect ratio.
Supported file types
| Type | Content-Type | Notes |
|---|---|---|
| JPEG | image/jpeg | Most common for phone camera receipts |
| PNG | image/png | Screenshots and scanned documents |
application/pdf | Invoices and formal documents |
The maximum file size is controlled by the organization's
expenseReceiptMaxSizesetting (in MB). Check this value from the organization'ssettingsobject to validate file size client-side before uploading.
Card transaction expenses
When an organization has card integrations, card transactions can be automatically converted into receipt expenses. These appear as draft expenses with the card transaction data pre-filled.
Card transaction expenses include:
- Pre-filled amount, currency, and date from the transaction
- A reference to the card transaction ID
meta.capabilities.canBeMerged: trueif the expense can be merged with a manually created expense
The user reviews the pre-filled data, attaches a receipt, selects a category, and submits — the same fields flow as a regular receipt expense.
Common integration patterns
Scan-first expense creation
The recommended flow for receipt expenses is scan-first: upload the receipt with action=scan, then initialize fields with the applyScanResult parameter. This pre-fills amount, tax, currency, date, and suggests categories — reducing manual entry to selecting the correct category and confirming values.
POST /v1/expense/content?action=scan&organizationId={id}
→ Extract scanResult.id and content id
GET /v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/fields?applyScanResult={scanResultId}
→ Fields pre-filled with OCR dataHandling foreign currency expenses
When the user selects a currency different from the organization's reimbursement currency, the exchange rate and reimbursement amount fields appear automatically after the requiresUpdate round-trip. Display the reimbursement amount prominently so the user knows the final amount they'll receive. The exchange rate is pre-filled from Findity's rate service but can be manually overridden if the organization allows it.
Email-based expense creation
Organizations can configure an expense email address (available in the organization's settings.expenseEmail). Receipts forwarded to this email are automatically created as draft expenses with OCR scanning applied. These appear in the user's draft list with the receipt already attached and data pre-filled.
Rendering the receipt alongside the form
Display the receipt image next to the expense form so users can reference it while filling in details. Fetch the image using the content ID from the verification.receiptAttachment field:
GET /v1/expense/content/{contentId}/image?size=600x800On mobile, show the receipt as a tappable thumbnail that opens a full-screen viewer.
Handling rejected expenses
When a receipt expense is rejected, load it with the fields endpoint using expenseRecordId. The response includes an INFORMATION field with informationType: error containing the approver's rejection comment. Display this prominently at the top of the form so the user knows what to fix before resubmitting.
Error handling
Common errors when working with receipt expenses:
| Status code | Error code | Description |
|---|---|---|
400 | INVALID_JSON_BODY | Failed to parse the request body. Verify your JSON payload. |
400 | INVALID_JSON_PROPERTY_VALUE | Missing or invalid type on verification — ensure type is set to ReceiptVerification. |
400 | NOT_A_RECEIPT_VERIFICATION_CATEGORY | The category ID is not valid for receipt expenses. Fetch categories for ReceiptVerification. |
400 | CONTENT_NOT_FOUND | The referenced content ID does not exist. Verify the receipt was uploaded successfully. |
400 | TOTAL_AMOUNT_REQUIRED | Total amount is missing. Ensure verification.totalAmount has a value. |
404 | EXPENSE_RECORD_NOT_FOUND | The expense ID does not exist or is not accessible by the current user. |
404 | CATEGORY_NOT_FOUND | The specified category does not exist in the organization. |
413 | CONTENT_TOO_LARGE | The uploaded file exceeds the organization's expenseReceiptMaxSize limit. |
Next steps
Full reference for the Fields system, control types, and the update cycle
API reference for uploading receipts and attachments
Guide for creating distance-based mileage expenses using Fields
Guide for creating per diem / subsistence allowance expenses
Updated about 4 hours ago
