Docs · integration
Two endpoints, and what each one will and will not accept
One reads charter context so an external system knows what it is posting against. The other stages APA expenses and promotes them into draft entries through the same validation a person goes through. That is the entire surface. If it is not on this page, it is not exposed.
Read this before you build against it
Every field on this page is held by a committed contract. A gate on every change reads the shape back out of the source and fails if it has drifted — a renamed field, a bumped version, a dropped promote, or a field quietly repointed at different data behind an unchanged name. A rename cannot reach you by accident. It can only reach you deliberately, in a change that rewrites the contract, and that is exactly the change worth arguing with me about.
What the gate does not do is call the API. It reads source, so nothing here is proven against a live tenant by an automated test. Verify your first round trip against a sandbox, and tell me you exist so a deliberate change gets discussed with you rather than announced at you.
One thing about that gate you should have straight before you rely on it: it is written, green and tested against a deliberately broken copy of itself, but it is still an open change rather than part of the main line. Today it holds the change that carries it, not every change. The limitations page tracks which of these have landed and which have not, and it will say so there before it says so anywhere else.
Endpoint shape
Both pages publish under /api/marvera/trident/v2.0/.
On Business Central online:
https://api.businesscentral.dynamics.com/v2.0/{tenantId}/{environment}/api/marvera/trident/v2.0/companies({companyId})/{entitySet}
On a self-hosted or container environment:
https://{server}:{port}/{instance}/api/marvera/trident/v2.0/companies({companyId})/{entitySet}
Company ids come from the standard Business Central companies endpoint. Both pages key on SystemId, so a record is addressed as {entitySet}({systemId}). Both are marked non-extensible: no other extension can add fields to them, so the shape you integrate against is the shape that ships.
Authentication is standard Business Central. Trident adds nothing to it and overrides nothing.
Permissions
Give the integration's service account Trident API and nothing else from Trident. It is not included in Admin, Ops or Read, and none of them include it.
| Object | Grant | Why it is there |
|---|---|---|
| APA Expense Import MV | Read, Insert, Modify | The staging table the integration writes to |
| APA Ledger Entry MV | Indirect read, indirect insert | Promotion can create the entry; the account cannot write one directly |
| Charter Header MV | Read | Charter context, and checking that a staged charter number exists |
| APA Expense Category MV | Read | Checking the category code, and whether it is blocked |
| Currency | Read | Checking the currency code |
| General Ledger Setup | Read | The local currency code, read when the currency snapshot is taken |
| Currency Exchange Rate | Read | Converting the expense amount at its posting date |
The indirect grant on the APA ledger is the part worth understanding. The service account cannot insert an APA entry directly. It can only cause one to exist by calling promote, which runs the same validation the journal runs. An integration cannot write an entry a person could not have written.
The last two rows are why a first attempt often fails with a permission error that looks unrelated to APA. Promotion snapshots the currency position at the posting date, which reads the local currency code and the rate.
charterContexts · read only
GET /companies({companyId})/charterContexts
Read-only in the strictest sense the platform allows: insert, modify and delete are disabled on the page and every field is non-editable. There is no way to change a charter through this endpoint.
| Field | Type | Notes |
|---|---|---|
systemId | GUID | The key |
no | string | The charter number |
description | string | |
status | enum | Open, Option, Confirmed, Invoiced, Completed, Cancelled, Active, Closed |
currencyCode | string | The charter's currency |
apaRequired | boolean | Whether the charter's pricing scheme requires APA |
startDate | date | |
endDate | date |
Use it to resolve a charter number before staging anything against it, to check apaRequired before sending APA at all, and to avoid posting into a charter that has been cancelled or closed. Standard OData filtering applies.
apaExpenseImports · staging and promotion
GET, POST and PATCH on /companies({companyId})/apaExpenseImports.
Insert one row per expense, then call promote on it. Rows are staged, not posted. Nothing reaches the APA ledger until promotion succeeds.
| Field | Type | Rule |
|---|---|---|
externalSource | string, 40 | Required, not blank. The system the expense came from |
externalId | string, 100 | Required, not blank. Your identifier for the expense |
charterNo | string, 20 | Must match an existing charter |
categoryCode | string, 20 | Must match an APA category that is not blocked |
postingDate | date | Required |
amount | decimal | Must be greater than zero |
currencyCode | string, 10 | Optional. If supplied it must exist |
description | string, 100 | |
vendorName | string, 100 | |
correlationId | string, 100 | Free-text trace reference, carried through untouched |
importStatus | enum | Read-only. Received, Rejected, Promoted, Duplicate |
errorDetails | string, 250 | Read-only. Why a row was rejected |
promotedEntryNo | integer | Read-only. The APA entry number created |
systemId | GUID | Read-only. The key |
A newly inserted row is Received. Trident also stamps the arrival time and the account that sent it; neither is exposed on the API page.
Promoting
POST /companies({companyId})/apaExpenseImports({systemId})/Microsoft.NAV.promote
Empty body. On success the staged row becomes a draft APA entry, not a reviewed one and not a posted one. It appears in the APA journal exactly as if someone had keyed it, and a person still reviews it.
Read the row back. Do not trust the HTTP status
The action reports success whenever it completes, including when it decided the row was invalid or a duplicate. importStatus is the real outcome, not the response code. This is the most common way to build a broken integration against this endpoint.
| importStatus | What happened | What to do |
|---|---|---|
| Promoted | A draft APA entry was created and promotedEntryNo holds its number | Nothing. Record the entry number on your side |
| Rejected | Validation failed. errorDetails lists every reason, separated by semicolons | Correct the row with PATCH and promote again. A corrected row promotes normally |
| Duplicate | Another row with the same source and external id was already promoted | Nothing. This is the guard working. Do not retry |
| Received | Unchanged, so the call did not reach promotion | Investigate. This should not happen |
Promoting a row that is already promoted does nothing and creates nothing, so retrying is safe.
What gets rejected
Every failing check is collected before anything is written, so errorDetails gives you all the problems at once rather than the first one: charter number missing or not found; category missing, not found, or blocked; posting date missing; amount not greater than zero; and a currency code that was supplied but does not exist.
Duplicate handling
The guard is externalSource plus externalId, and it only fires against rows that were actually promoted. Two rows carrying the same pair can both sit in staging; the second one promoted becomes Duplicate rather than creating a second APA entry.
So you can retry a failed send without deduplicating on your side, as long as the same expense always carries the same externalId. Send a different one and you get two APA entries, and Trident will not know they were the same expense.
What is not exposed
No endpoint exists for writing charters, or for vessels, payment templates, payment schedules, settlement, closeout, commercial evidence, or the APA ledger itself. Reading APA entries back is not available through the API; the ledger is reachable through the Business Central client and through the finance review report.
Bulk promotion exists in the code behind the endpoint and is not published, because nobody has needed it yet.
If your integration needs one of these, say which and why. Adding an endpoint is cheap. Adding the wrong one and supporting it forever is not. Write to me.