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

ConceptDescription
OCR scanningUpload a receipt image before creating the expense. The API extracts amount, tax, currency, date, and suggests categories — pre-filling the form.
CategoriesEach organization defines expense categories (e.g., Travel, Meals, Office supplies). Changing the category can alter which fields are visible.
Tax / VATTax fields are calculated server-side based on category, country, and amount. Your client displays them but doesn't compute them.
RepresentationEntertainment/representation expenses have additional fields: guest lists, purpose, and representation type. These appear dynamically when the category requires it.
ContentReceipt images and PDF attachments are uploaded separately via the Content API and referenced by ID in the fields.

API endpoints

OperationMethodEndpoint
Upload receipt (with OCR)POST/v1/expense/content?action=scan&organizationId={id}
Upload receipt (without OCR)POST/v1/expense/content?organizationId={id}
Initialize fieldsGET/v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/fields
Update fieldsPUT/v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/fields
Create expensePOST/v1/expense/expenses?format=fields&organizationId={id}&expenseType=ReceiptVerification
Update expensePUT/v1/expense/expenses/{expenseId}?format=fields
Get categoriesGET/v1/expense/me/organizations/{id}/expensetypes/ReceiptVerification/categories
Get currenciesGET/v1/expense/me/organizations/{id}/currencies
Get payment typesGET/v1/expense/me/organizations/{id}/paymenttypes
Retrieve receipt imageGET/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.jpg

Example 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

FieldTypeDescription
idstringScan result ID — pass to fields initialization and category lookup
amountnumberExtracted total amount
taxAmountnumberExtracted tax/VAT amount
currencystringDetected currency code
purchaseDatestringDetected purchase date (ISO 8601)
categoryIdsstring[]Up to 3 suggested category IDs, ordered by confidence
categoryIdstringTop 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 propertycontrolTypeDescription
idTEXTExpense ID (hidden, system field)
categoryIdLISTExpense category — requiresUpdate: true
verification.commentMULTI_LINE_TEXTUser comment
verification.descriptionTEXTExpense description
expenseReportIdLISTAssign to an expense report
verification.receiptAttachmentCONTENT_HOLDERReceipt image or PDF attachment
verification.purchaseDateDATEDate of purchase — requiresUpdate: true
verification.currencyCodeLISTTransaction currency — requiresUpdate: true
verification.totalAmountDOUBLETotal amount including tax (shown as "Amount incl. VAT")
verification.taxAmountDOUBLETax / VAT amount
verification.paymentTypeIdLISTPayment method (cash, corporate card, etc.)
verification.verificationNumberTEXTSystem-generated verification number (read-only)
verification.storeNameTEXTStore or merchant name
verification.exchangeRateDOUBLEExchange rate (appears when foreign currency selected)
Custom dimension fieldsLISTOrganization-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 propertycontrolTypeDescription
verification.taxAmountDOUBLETax / VAT amount — may be auto-calculated
verification.nonDeductibleVatDOUBLENon-deductible VAT portion
verification.taxPercentageLISTTax rate selector (where applicable)
📘

Tax fields are computed server-side. When the user changes totalAmount, currencyCode, or categoryId, 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 propertycontrolTypeDescription
verification.currencyCodeLISTTransaction currency — requiresUpdate: true
verification.exchangeRateDOUBLEExchange 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 propertycontrolTypeDescription
verification.representation.typeLISTType of representation (internal, external, etc.) — requiresUpdate: true
verification.representation.guestsGUEST_LISTList of guests with name and company
verification.representation.numberOfGuestsINTEGERTotal number of guests
verification.representation.numberOfExternalGuestsINTEGERNumber of external guests
verification.representation.placeTEXTPlace or venue name
verification.representation.includesLiquorSWITCHWhether liquor/wine/strong beer was included
verification.representation.guestListAttachedSWITCHWhether to attach a guest list file — requiresUpdate: true
verification.representation.guestListAttachmentCONTENT_HOLDERAttached 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:

FieldWhat changes on update
categoryIdTax rules, visible dimensions, representation fields, VAT logic
verification.currencyCodeExchange rate, reimbursement amount
verification.totalAmountTax recalculation (in some configurations)
verification.representation.typeRepresentation field visibility
verification.paymentTypeIdReimbursement-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.pdf

Retrieving 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

TypeContent-TypeNotes
JPEGimage/jpegMost common for phone camera receipts
PNGimage/pngScreenshots and scanned documents
PDFapplication/pdfInvoices and formal documents
⚠️

The maximum file size is controlled by the organization's expenseReceiptMaxSize setting (in MB). Check this value from the organization's settings object 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: true if 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 data
Handling 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=600x800

On 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 codeError codeDescription
400INVALID_JSON_BODYFailed to parse the request body. Verify your JSON payload.
400INVALID_JSON_PROPERTY_VALUEMissing or invalid type on verification — ensure type is set to ReceiptVerification.
400NOT_A_RECEIPT_VERIFICATION_CATEGORYThe category ID is not valid for receipt expenses. Fetch categories for ReceiptVerification.
400CONTENT_NOT_FOUNDThe referenced content ID does not exist. Verify the receipt was uploaded successfully.
400TOTAL_AMOUNT_REQUIREDTotal amount is missing. Ensure verification.totalAmount has a value.
404EXPENSE_RECORD_NOT_FOUNDThe expense ID does not exist or is not accessible by the current user.
404CATEGORY_NOT_FOUNDThe specified category does not exist in the organization.
413CONTENT_TOO_LARGEThe uploaded file exceeds the organization's expenseReceiptMaxSize limit.

Next steps