# Expenses _(Realtime-enabled)_

`GET` `POST` `PATCH` `DELETE` _(Table: EONE Transaction)_

```
eOne/core/v1.0/expenses
```

Import employee expense reports (e.g. from Concur) as their own header/line document, a header with an `expenseLines` child collection, backed by a dedicated staging table so it can be reviewed and approved in Business Central before it reaches the ledger. Each line is one expense against an expense G/L account; the header records how the report was paid.

## Report name, number and key

One expense report is one document, and three header fields carry its identity:

- **`description`.** The report name, for example `August Travel - Tokyo`. SmartConnect carries it onto the journal lines, so it becomes the description of the resulting general ledger entries. That is where an accountant reads it.
- **`externalDocumentNumber`.** The report number from your expense system, for example a Concur report id. Keep it human-readable, because it is what gets quoted when someone queries the report.
- **`integrationId`.** The report key. It is carried onto every journal line the report produces, and it is what you look the report up by afterwards. See [Processed Documents](https://eonesolutions.github.io/eone-bc-integration/core-processedDocuments.md).

The in-product *eOne Expenses* page shows `description` as **Report Name**, so the client and the API agree on which field is which.

## Reimbursement type

`reimbursementType` records how the report is settled with the person who incurred it, as your expense system already classified it. The values are:

- **Reimbursable.** The employee paid out of pocket and is owed the money back.
- **Company Paid.** The company paid directly, so nothing is owed to the employee.
- **Card Paid.** Paid on a corporate card, tracked separately so the card statement can be reconciled.

Leave it blank when your source does not classify reports. It is a classification, not a posting instruction: the balancing account below still decides where the credit lands.

> **Caution:** A **Reimbursable** report must have `balancingAccountType` set to **Employee**. The classification and the balancing account come from different mappings, so they disagree exactly when one of them is wrong. A Reimbursable report credited to a bank account would look settled while the employee is still out of pocket. Both `validate` and `process` reject it and name the account type they found, so you see the mapping error before anything posts.

## How it was paid: the balancing account

The header's `balancingAccountType` + `balancingAccountNumber` are the **credit** side, i.e. how the expense was settled, the same idea as a Continia payment type or the Expense Agent's payment method:

- **Employee.** Out-of-pocket; credits the employee, creating a reimbursement in the Employee ledger.
- **Bank Account.** Company card or cash already paid.
- **Vendor.** For example, a corporate-card provider.
- **G/L Account.** A clearing/holding account.

## Shape

POST the header, then POST one line per expense under `expenses(<id>)/expenseLines`. Dimensions are **per line** here, since expenses are split by cost centre (see [Expense Lines](https://eonesolutions.github.io/eone-bc-integration/core-expenseLines.md) for the line fields). `process` writes one debit journal line per expense line plus a single balancing credit line to the account above, then clears the staging document.

## Processing is configurable

How a staged expense reaches the ledger is set on the **eOne Integration Setup** page (Journal Wrappers section): **Manual** (call `process`, or use the in-product *eOne Expenses* page), **Auto Draft to Journal**, or **Auto Post**. Reports arriving pre-approved from the source pair naturally with Auto Post.

## Bound actions

Invoke with `POST /expenses(<id>)/Microsoft.NAV.<action>`:

- `validate`: re-runs the document's checks (accounts, balancing account, amounts) **without** processing it, reporting any problem in `status` / `errorMessage`.
- `process`: writes one debit journal line per expense line plus a single balancing credit line, then clears the staging document (unposted draft, or posted, per the Setup policy above).
- `post`: processes the expense **and posts it**, whatever the Setup policy says. Use it when SmartConnect decides that this one expense goes to the ledger now, rather than an administrator putting the whole endpoint on Auto Post. Only the lines this expense just wrote are posted, so other drafts sitting in the same journal batch are left alone, and the approval gate applies exactly as it does to `process`.
- `reopen`: returns a failed expense to Open and clears its error, handing it back to the background job. A expense that hits `status` = Error is not collected again on its own, so on an Auto endpoint - where by definition nothing calls `process` - this is how a corrected expense gets moving. It refuses a expense that is pending approval, exactly as the Business Central action does.

`process` and `post` return no body. The staged expense deletes itself the moment its journal lines exist, so there is nothing to echo, and the outcome lives on the [Processed Documents](https://eonesolutions.github.io/eone-bc-integration/core-processedDocuments.md) row the same call wrote. Fetch it by the `integrationId` you sent the expense with - `processedDocuments?$filter=integrationId eq '<your id>'` - and read `postedDocumentNumber`, `postedPostingDate`, `glTransactionNumber` and `status`; after `post` they are already filled in. `validate` writes nothing and points back at the staged expense. A rejected call returns the error and leaves the expense in staging, with `errorMessage` describing the problem, ready to correct and retry. See [Journal Wrappers](https://eonesolutions.github.io/eone-bc-integration/journal-wrappers.md).

> **Note:** Approval is not done through the API. When an endpoint has **Requires Approval** set and a Business Central approval workflow is enabled for eOne documents, an expense must be **Approved** before it processes. A licensed user sends it for approval and approves it in Business Central, on the *eOne Expenses* page (**Send Approval Request**, then the standard Approve/Reject/Delegate), backed by Approval User Setup. The read-only `status` (Open → Pending Approval → Approved) and `errorMessage` fields report where the document is; a failed automatic attempt lands `status` = Error with the reason in `errorMessage`.

### What a PATCH may do

Two rules apply to every write, on the header and on its lines and applications alike.

**A expense under approval cannot be changed.** Once `status` is Pending Approval or Approved, a PATCH, a line append or a delete is rejected. What the approver signed off on is what posts. Call `reopen` first if the expense genuinely has to change; that is an explicit act rather than a silent revocation, and it leaves no orphaned approval entry behind.

**Deleting is also a setup choice.** A DELETE of a staged expense is refused when the endpoint's **Allow Manual Delete** switch is off on the eOne Integration Setup page. It is on by default, so nothing changes until an administrator turns it off to mark the whole document family integration-owned. The approval rule above holds regardless of the switch.

**Correcting a failed expense clears its error.** Any successful write to a expense whose `status` is Error returns it to Open and empties `errorMessage`, so it becomes visible to the background job again - a line or application write counts, because the failure is usually in a line. The full text of every failed attempt is kept separately and is not lost when the live message is cleared. Awaiting Invoice is left alone: that one heals itself when the invoice posts.

## Status writeback: telling your expense system what happened

Every stage of the report's life in Business Central has a signal SmartConnect can read. The map carries each one back to Concur, Expensify or any other source system:

- **Received**: the POST response, and the staged document's read-only `status` and `errorMessage` fields while it waits or fails.
- **Processed and posted**: the report's row on the `processedDocuments` endpoint. The row appears when the document is processed. Its `status` flips to **Posted** when the journal actually posts, carrying the final `postedDocumentNumber` and `glTransactionNumber`. The endpoint supports webhooks, so a map subscribes once and reacts to both moments.
- **Paid**: the reimbursement's row on the `postedEmployeePayments` endpoint (also webhook-enabled), keyed by the same `employeeIntegrationId` used on the report.

For Concur, SmartConnect sends the Financial Integration Service acknowledgement as soon as a document is fetched, because Concur can recall an unacknowledged document. The posting confirmation follows when `processedDocuments` reports the report, and the payment confirmation when the reimbursement posts. For Expensify, one update call marks the report **Reimbursed** at whichever of those moments matches your policy.

## Sending the same report twice

Expense systems re-export. Expensify re-sends anything not stamped with your export label, a Concur recall replays a document, and a timeout makes any map retry a POST. Two layers keep a re-send from becoming a duplicate posting:

- **In your export**: mark what you have already pulled. Expensify's `markAsExported` action with a per-environment label, combined with the `markedAsExported` filter, makes Expensify itself skip anything already sent to this Business Central environment.
- **In Business Central**: the duplicate gate. With **Reject Duplicate Integration Ids** on (the default), a report whose `integrationId` is already staged or already processed is refused, and the error names the document it was first imported as. The map surfaces that as a per-record exception instead of posting the report twice.

To deliberately import a report again, for example after a correction in the source system, delete its row from the **eOne Processed Documents** page and re-send it. The gate then accepts the new version.

## Expenses fields

_API column → BC field._

**Legend**

- _(req)_ required on create
- _(res)_ resolver
- _(cur)_ currency-translated
- _(ro)_ read-only
- _(ext)_ added to this endpoint by another extension (BC field name shown; its API column name is not known here)

| API | BC field | Notes |
| --- | --- | --- |
| `id` | SystemId | _(ro)_ |
| `bcCompanyId` | bcCompanyId | The company this row belongs to - the same GUID that appears in the request URL. Present on every endpoint so a store pooling several companies can keep them apart without relying on which URL it called. _(ro)_ |
| `bcCompanyName` | bcCompanyName | The name of the company this row belongs to. _(ro)_ |
| `documentNumber` | Document No. | Expense document number. |
| `postingDate` | Posting Date | Date the expense posts to. |
| `pricesIncludeTax` | Prices Include Tax | Whether line amounts already include tax. Off (default): a line's amount is the taxable base and its taxAmount is added on top, so the line costs the document amount + tax. On: the tax is already inside the line amount and is carved back out, so the line costs the document its amount. This says only what the numbers MEAN - the tax figure itself always comes from the line's taxAmount, never from Business Central's own rate. See Journal Wrappers. |
| `documentDate` | Document Date | The document's own date; defaults to the posting date when left blank. |
| `description` | Description | The expense report name, e.g. "August Travel - Tokyo". It flows onto the journal lines and so onto the Description of the resulting G/L entries, which is where an accountant reads it. Send the report NUMBER in externalDocumentNumber and the report key in integrationId. |
| `reimbursementType` | Reimbursement Type | How the report is settled with the employee: Reimbursable (out of pocket), Company Paid, or Card Paid. Reimbursable requires balancingAccountType Employee, so a mis-mapped report is rejected instead of paying the money somewhere the employee never sees it. Leave blank if your source does not classify reports. |
| `balancingAccountType` | Bal. Account Type | How the expense was paid: Employee (reimbursed), Bank Account (company card/cash), Vendor, or G/L Account. |
| `balancingAccountNumber` | Bal. Account No. | The employee, bank, vendor or G/L account the credit posts to. |
| `employeeIntegrationId` | employeeIntegrationId | Resolve the employee the report is reimbursed to by their Integration Id, instead of sending balancingAccountType and balancingAccountNumber. Sets the balancing account to that employee. See employees. _(res)_ |
| `currencyCode` | currencyCode | ISO code; blank is LCY. _(cur)_ |
| `externalDocumentNumber` | External Document No. | The report number from your expense system, e.g. a Concur report id. It is what an accountant quotes when querying the report, so keep it human-readable and put the report name in description. |
| `yourReference` | Your Reference | The source report reference; flows onto the journal lines and posted entries. |
| `expectedLineCount` | Expected Line Count | How many lines or applications this document will have when it is complete. Set it on the header and the document is not processed until that many have arrived, instead of waiting out the five-minute idle window - so a document built over several calls is never processed half-built, and a complete one goes through on the next cycle. Leave it blank (the default) to keep the idle-window behaviour. |
| `declaredTotalAmount` | Declared Total Amount | Optional second check on top of expectedLineCount: the total you believe the lines add up to. When set and the arrived lines do not match it, the document fails with the difference stated rather than posting. Leave it zero to skip the check. This does not change the header amount, which stays a ceiling. |
| `integrationId` | EONE Integration Id | The report key - your unique key for this expense report. It is carried onto every resulting journal line and is what you look the report up by in processedDocuments afterwards. |
| `journalTemplateName` | Journal Template Name | General journal template the draft lines are written to. |
| `journalBatchName` | Journal Batch Name | General journal batch the draft lines are written to. |
| `totalAmount` | Total Amount | Sum of the expense lines. _(ro)_ |
| `status` | Status | Workflow state: Open, Pending Approval, Approved or Error. _(ro)_ |
| `errorMessage` | Error Message | Why the last automatic processing attempt failed. _(ro)_ |
| `createdDateTime` | SystemCreatedAt | When the integration imported this document into staging. _(ro)_ |
| `errorDateTime` | Error Date-Time | When the document last entered Error. Cleared when it is approved or reopened. _(ro)_ |
| `lastModifiedDateTime` | SystemModifiedAt | _(ro)_ |

## Related pages

Collections nested under this endpoint:

- [Expense Lines](https://eonesolutions.github.io/eone-bc-integration/core-expenseLines.md)
