Skip to content

Expenses Realtime-enabled — SmartConnect can trigger on changesRealtime-enabled

GETPOSTPATCHDELETETable: 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.

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.

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 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 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.

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.