# Transportial OTMS API

Welcome to the Transportial API. It\'s the full API access for the OTMS Transportatial application. Every feature that is available (and more)
            in the UI is available here. It\'s our mission to facilitate open, fast, enterprise grade architecture for transportation.
        

Version: 1.0.0

## Servers

Test environment
```
https://test.api.otms.transportial.com/api
```

Production
```
https://api.otms.transportial.com/api
```

## Security

### apiKey

Type: apiKey
In: header
Name: x-api-key

### bearer

Type: http
Scheme: bearer
Bearer Format: JWT

### apiSecret

Type: apiKey
In: header
Name: x-api-secret

## Download OpenAPI description

[Transportial OTMS API](https://docs.transportial.com/_bundle/apis/index.yaml)

## General

Reference data and utilities shared by every module. Global, platform-independent reference data (languages with their number, date and measurement conventions; currencies with lower-case ISO 4217 codes; industries; the route-permission catalogue; ADR Table A dangerous-goods entries with on-demand cached translations) is public or available to any authenticated user. Platform-scoped financial master data (tax rates and pricing categories) can be listed, searched, created, updated, soft-deleted or replaced as a whole set. Utilities include global search across vehicles, transport orders, businesses, locations and chauffeurs, empty entity templates, the audit history of versioned entities and their children, and building JSON from integration mapping paths.

### Search tax rates

 - [POST /taxRates/{offset}/{amount}](https://docs.transportial.com/apis/general/search:taxrates.md): Searches the tax rates of your platform and returns one page of matches plus the total number of matches,
ordered by sortBy or else most recently updated first. Archived and deleted tax rates are never returned.

query is matched case-insensitively (contains) against name, credit code, debit code, description and
jurisdiction; active optionally restricts the result to active or inactive tax rates. When the search itself
fails an empty page with total 0 is returned.

Permission: any authenticated user; no specific permission is required.

Errors: 400 when the body is not valid JSON or offset is not a number.

### List industries

 - [GET /industries](https://docs.transportial.com/apis/general/get:industries.md): Returns every industry category known to the installation, unpaged. Industries are global reference
data (not platform-scoped) used to classify businesses, for example during sign-up.

Permission: Public, no authentication.

Errors: none specific to this endpoint; when the lookup fails an empty list is returned.

### List all route permissions

 - [GET /permissions](https://docs.transportial.com/apis/general/get:permissions:catalogue.md): Returns the catalogue of route permissions known to the installation (for example get:trips or
update:vehicle), unpaged. These are the names that can be granted to a role; object-level permissions are
not included. The catalogue is global and is completed automatically at startup with every permission name
used by the API.

Permission: any authenticated user; no specific permission is required.

Errors: none specific to this endpoint; when the lookup fails an empty list is returned.

### Update a pricing category

 - [PUT /pricingCategory/{id}](https://docs.transportial.com/apis/general/update:pricingcategory.md): Updates a pricing category of your platform. The body is merged onto the stored category: fields that are sent
replace the stored values, omitted fields keep them. The category to update is looked up by the id in the
body, so always send the same id as in the path.

When no pricing category with this id exists on your platform the body is saved as a new category (upsert).

Permission: update:pricingCategory, or a role above level 600 (the roles that see the platform settings screen).

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a
pricing category of another platform.

### Delete a pricing category

 - [DELETE /pricingCategory/{id}](https://docs.transportial.com/apis/general/delete:pricingcategory.md): Soft-deletes a pricing category of your platform: it is marked deleted and no longer listed or searchable.

Permission: delete:pricingCategory, or a role above level 600 (the roles that see the platform settings screen).

Errors: 400 when no pricing category with this id exists on your platform (already deleted and
archived categories count as not found).

### Create a tax rate

 - [POST /taxRate](https://docs.transportial.com/apis/general/create:taxrate.md): Creates a tax rate (VAT rate) on your platform. The new tax rate is owned by your platform and can then be
referenced by money amounts, quote and invoice lines and pricing.

Permission: create:taxRate, or a role above level 600 (the roles that see the platform settings screen).

Errors: 400 when the body is not valid JSON or a field has the wrong type.

### List pricing categories

 - [GET /pricingCategories](https://docs.transportial.com/apis/general/get:pricingcategories.md): Returns the pricing categories of your platform, unpaged. Pricing categories classify price and cost lines
(for example freight, fuel surcharge, tolls) and carry the ledger credit/debit codes used when invoices are
exported to accounting.

By default only enabled categories are returned; pass includeDisabled=true to include disabled ones.
Archived and deleted categories are never returned.

Permission: any authenticated user; no specific permission is required.

Errors: 400 when includeDisabled is not exactly true or false.

### Replace all pricing categories

 - [PUT /pricingCategories](https://docs.transportial.com/apis/general/put:pricingcategories.md): Replaces the complete set of pricing categories of your platform with the list in the body, matched by id:

- pricing categories of your platform whose id is not in the list are soft-deleted (enabled and disabled ones alike);
- list entries whose id matches an existing pricing category update that category in place;
- list entries without a matching id (for example a blank id) are created on your platform.

The response echoes the submitted list.

Permission: update:pricingCategories, or a role above level 600 (the roles that see the platform settings screen).

Errors: 400 when the body is not a JSON array of pricing categories.

### Get an empty entity of a type

 - [GET /entity/{entityType}](https://docs.transportial.com/apis/general/get:entity:template.md): Returns a new, unsaved instance of the given entity type with all default values filled in, serialized
exactly as the API returns that entity. Use it as a template to see the full JSON structure of an entity (for
example when building integration mappings) or as a starting point for a create request. Nothing is stored.

entityType is one of the entity type names of the API (see the EntityType schema), for example trip,
transportOrder, consignment, vehicle or location.

Permission: any authenticated user; no specific permission is required.

Errors: 400 when entityType is not a known entity type.

### List tax rates

 - [GET /taxRates](https://docs.transportial.com/apis/general/get:taxrates.md): Returns all tax rates (VAT rates) of your platform, unpaged, most recently updated first. Archived and
deleted tax rates are excluded; inactive ones (active false) are included.

Tax rates are referenced by money amounts, quote and invoice lines and pricing to calculate tax.

Permission: any authenticated user; no specific permission is required.

Errors: none specific to this endpoint; when the lookup fails an empty list is returned.

### Replace all tax rates

 - [PUT /taxRates](https://docs.transportial.com/apis/general/put:taxrates.md): Replaces the complete set of tax rates of your platform with the list in the body, matched by id:

- tax rates of your platform whose id is not in the list are soft-deleted;
- list entries whose id matches an existing tax rate update that tax rate in place;
- list entries without a matching id (for example a blank id) are created on your platform.

The response echoes the submitted list.

Permission: update:taxRates, or a role above level 600 (the roles that see the platform settings screen).

Errors: 400 when the body is not a JSON array of tax rates.

### Get the ADR Table A entries of a UN number

 - [GET /adr/un-numbers/{unNumber}](https://docs.transportial.com/apis/general/get:adr:unnumber.md): Returns every ADR Table A variant of one UN number (one entry per packing group / classification code,
ordered by packing group), each with a fully populated adr object ready to copy onto goods or transport
equipment.

The UN number is left-padded with zeros to four digits, so 4 and 0004 both match UN 0004. When
language is given and no description exists in that language yet, the substance name is machine-translated
once through the platform's translation provider and cached on the entry for every later request; when the
translation fails the English description is returned.

An unknown UN number is not an error: the response is a successful empty list with totalResults 0.

Permission: Public, no authentication.

Errors: none specific to this endpoint.

### Create a pricing category

 - [POST /pricingCategory](https://docs.transportial.com/apis/general/create:pricingcategory.md): Creates a pricing category on your platform. Pricing categories classify price and cost lines and carry the
ledger credit/debit codes used when invoices are exported to accounting. A new category is disabled unless
enabled is sent as true.

Permission: create:pricingCategory, or a role above level 600 (the roles that see the platform settings screen).

Errors: 400 when the body is not valid JSON or a field has the wrong type.

### Get the change history of an entity

 - [GET /entity:history/{entityType}/{id}/{offset}/{amount}](https://docs.transportial.com/apis/general/get:entity:history.md): Returns one page of the audit history of an entity of your platform, newest revision first, plus the total
number of revisions. The history includes revisions of the entity itself and of its child entities (for example
the actions of a trip), each entry naming the entity type and id it belongs to.

Every entry lists the action (ADD, MOD or DEL), the names of the changed fields and, per changed field,
its old and new value rendered as text, together with the user who made the change and the revision date.

When the entity is not found on your platform, or its type keeps no history, the response is HTTP 200 with
success false and the message "Cannot fetch versions of a non-versioned object".

Permission: any authenticated user; no specific permission is required.

Errors: 400 when entityType is not a known entity type.

### List currencies

 - [GET /currencies](https://docs.transportial.com/apis/general/get:currencies.md): Returns every currency known to the installation, unpaged. Currencies are global reference data
(not platform-scoped): every installation seeds at least EUR, USD, CAD and MXN, with lower-case ISO 4217
short codes (eur, usd). Disabled currencies are included; check enabled before offering one for selection.

Use the returned objects (or just their id or short code) wherever a currency is expected, for example in
the currency of a money amount.

Permission: Public, no authentication.

Errors: none specific to this endpoint; when the lookup fails an empty list is returned.

### Search ADR UN numbers

 - [GET /adr/un-numbers](https://docs.transportial.com/apis/general/search:adr:unnumbers.md): Searches the ADR Table A dangerous-goods reference data (global, the same for every platform) and
returns one page of entries plus the total number of matches.

search matches UN numbers that start with the given text, or substance names that contain it
(case-insensitive); class narrows the result to one ADR class. Results are ordered by UN number, then
packing group. A UN number can occur several times (one entry per packing group / classification code
variant, as in ADR chapter 3.2).

Every entry carries a ready-to-use adr object that can be copied onto goods or transport equipment as-is.
Its description is taken in language when that translation is already cached, otherwise in English; this
endpoint never triggers a translation (use GET /adr/un-numbers/{unNumber} for that).

Permission: Public, no authentication.

Errors: none specific to this endpoint; out-of-range paging values are clamped.

### Update a tax rate

 - [PUT /taxRate/{id}](https://docs.transportial.com/apis/general/update:taxrate.md): Updates a tax rate of your platform. The body is merged onto the stored tax rate: fields that are sent replace
the stored values, omitted fields keep them. The tax rate to update is looked up by the id in the body, so
always send the same id as in the path.

When no tax rate with this id exists on your platform the body is saved as a new tax rate (upsert).
Money amounts and lines that already embed a copy of the tax rate keep their copy.

Permission: update:taxRate, or a role above level 600 (the roles that see the platform settings screen).

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a tax rate
of another platform.

### Delete a tax rate

 - [DELETE /taxRate/{id}](https://docs.transportial.com/apis/general/delete:taxrate.md): Soft-deletes a tax rate of your platform: it is marked deleted and no longer listed or searchable. Money
amounts and lines that already embed a copy of the tax rate keep their copy.

Permission: delete:taxRate, or a role above level 600 (the roles that see the platform settings screen).

Errors: 400 when no tax rate with this id exists on your platform (already deleted and archived tax
rates count as not found).

### List languages

 - [GET /languages](https://docs.transportial.com/apis/general/get:languages.md): Returns every language the installation offers, unpaged. Languages are global reference data (not
platform-scoped). Each language carries its code (EN, NL, DE, ... or a regional BCP 47 code such as
en-US), number formatting (decimal and thousand delimiters), date format and measurement system
(metric or imperial).

Use a returned language (or its id) wherever a language is expected, for example on users, businesses
or document templates; it drives translations and the formatting of numbers and dates.

Permission: Public, no authentication.

Errors: none specific to this endpoint; when the lookup fails an empty list is returned.

### Search pricing categories

 - [POST /pricingCategories/{offset}/{amount}](https://docs.transportial.com/apis/general/search:pricingcategories.md): Searches the pricing categories of your platform and returns one page of matches plus the total number of
matches, ordered by sortBy or else most recently updated first. Archived and deleted categories are never returned.

query is matched case-insensitively (contains) against name, credit code and debit code; enabled and
tripRevenue optionally restrict the result to categories with that flag value. Unlike
GET /pricingCategories, disabled categories are included unless includeDisabled=false is passed. When the
search itself fails an empty page with total 0 is returned.

Permission: any authenticated user; no specific permission is required.

Errors: 400 when the body is not valid JSON or offset is not a number.

### Search across entity types

 - [POST /general/{offset}/{amount}](https://docs.transportial.com/apis/general/search:general.md): Global search: finds vehicles, transport orders, businesses, locations and chauffeurs of your platform whose
search string matches query (phrase match with slop 10) and returns one page of hits plus the total number of
hits. Archived and deleted entities are excluded; hits are ordered by creation date, newest first.

Each hit is returned as { "entityType": ..., "entity": ... } so results of different types can be told apart.
Users with an external account type (customer portal users) only search their own transport orders.

When the search itself fails an empty page with total 0 is returned.

Permission: any authenticated user; no specific permission is required.

Errors: 400 when the body is not valid JSON, or offset or amount is not a number.

### Build a JSON document from path/value pairs

 - [PUT /mappingToJson](https://docs.transportial.com/apis/general/create:json:frommapping.md): Builds a JSON document from a flat map of mapping paths to values, the same way integration mappings write
their output. Each key is a dotted path (a.b.c; array positions as [n], for example documents.[1].name)
and each value is written at that path, creating intermediate objects and arrays as needed. Entries are applied
in the order they appear in the body.

The result is returned as a JSON string in value. Nothing is stored. Accepts both PUT and POST.

Permission: any authenticated user; no specific permission is required.

Errors: 400 when the body is not valid JSON or mapping is not an object.

## Async

Lets a client poll for requests submitted for asynchronous execution with the header `async: true`. Submitting returns a requestId straight away; the request is tracked as an integration task (queue, processing, success, error) and its response body is stored in the request log. Use the status endpoint to poll and the result endpoint to fetch the stored response once it has succeeded.

### Get the result of a completed async request

 - [GET /async/result/{requestId}](https://docs.transportial.com/apis/async/get:async:result.md): Returns the outcome of a request that was submitted for asynchronous execution, once its status (see GET /async/status/{requestId}) is success.

The result is the response body the original request produced, returned as-is when it is a JSON object, or wrapped as { "result": "" } when it is not. It is not wrapped in the usual success envelope, so its shape is that of the original endpoint's response. Read-only.

Permission: Public, no authentication (the request id acts as the only key).

Errors: 404 (ASYNC_RESULT_NOT_AVAILABLE) when the request is unknown, has not finished successfully yet, or no result was stored.

### Get the status of an async request

 - [GET /async/status/{requestId}](https://docs.transportial.com/apis/async/get:async:status.md): Returns the execution status of a request that was submitted for asynchronous execution (a request sent with the header async: true to an async-capable route, which answers immediately with { "requestId": ..., "status": "submitted" }).

The status is read from the integration task that tracks the request: queue, processing, success or error (with errorMessage). Once the status is success, fetch the outcome with GET /async/result/{requestId}. The response is a bare JSON object, not wrapped in the usual success envelope. Read-only.

Permission: Public, no authentication (the request id acts as the only key).

Errors: 404 (ASYNC_REQUEST_NOT_FOUND) when no request with this id is known.

## TQL

Transportial Query Language: a read-only JSON query language that selects, filters, sorts and pages one entity type. Field names must come from an allow-list per entity type (persistent properties plus to-one associations, up to five levels deep, without secrets or hidden fields), which the fields endpoints expose. Queries are restricted to the caller's platform; joins, groupBy and having are accepted but not yet run. Pricing TQL modules embed the same query model.

### List queryable fields starting with a keyword

 - [GET /tql/fields/{entityType}/{keyword}/{offset}](https://docs.transportial.com/apis/tql/get:tql:fields:prefix.md): Returns the queryable field paths of an entity type whose name starts with keyword (case-sensitive), from the same allow-list as GET /tql/fields/{entityType}.

Despite its name, the last path segment is used as a maximum number of results, not as an offset: the matching fields are taken in list order, the first match is always skipped, and at most offset entries are returned (0 returns an empty list). Prefer GET /tql/fields/{entityType}/{keyword}/{offset}/{amount} for real pagination. totalResults is always 0 on this endpoint. An unknown entityType silently falls back to location.

Permission: tql:fields

### Execute a TQL query

 - [POST /tql/query](https://docs.transportial.com/apis/tql/execute:tql:query.md): Runs a Transportial Query Language (TQL) query against one entity type and returns the matching rows of your platform.

The query is translated to HQL on entityType, always restricted to rows whose primaryPlatformId is your current platform. Every field name in fields, filters and sortFields is checked against the entity's allow-list (see GET /tql/fields/{entityType}); names that are not on it are silently dropped rather than rejected. Nested filters are combined with AND ( ... ) and are evaluated up to three levels deep.

- With an empty fields list each result is the full serialised entity; with one field each result is that value; with several fields each result is an array of the values in fields order. Rows whose projection is null are left out.
- sortFields are applied in order with one sortDirection for all of them.
- limit and offset page the result. There is no default limit, so omit them only for small result sets.
- joins, groupBy, having and Field.groupByAggregate are accepted but currently ignored.

Read-only: nothing is created or changed.

Permission: tql:query

Errors: a body that cannot be parsed into a query (unknown entityType, operator or value type, or a join without onLeft/onRight) fails the request.

### List queryable fields of an entity type

 - [GET /tql/fields/{entityType}](https://docs.transportial.com/apis/tql/get:tql:fields.md): Returns every field path that can be used in a TQL query (fields, filters, sortFields) on the given entity type.

Paths are the persistent properties of the entity plus the properties of its to-one associations, written dotted (vehicle.licensePlate) and nested up to five levels deep. Collection associations are never included because HQL cannot address them with a dotted path. Fields named primaryPlatformId, password, key or token, and fields hidden from JSON (@JsonIgnore, or @ExcludeFromJSON for this caller), are left out. This list is exactly the allow-list POST /tql/query enforces.

The list is computed once per entity type and then cached on the server, so the order of the fields is not guaranteed. An unknown entityType silently falls back to location. No pagination: totalResults is the number of fields returned.

Permission: tql:fields

### Search queryable fields of an entity type

 - [GET /tql/fields/{entityType}/{keyword}/{offset}/{amount}](https://docs.transportial.com/apis/tql/search:tql:fields.md): Returns one page of the queryable field paths of an entity type whose name contains keyword (case-insensitive), from the same allow-list as GET /tql/fields/{entityType}.

offset skips that many matches and amount (default 20) caps the page; larger amounts are clamped to the server maximum page size (MAX_PAGE_SIZE, default 1000). totalResults is the number of fields on the returned page, not the total number of matches. Once the field list of the entity type is cached on the server, the order of the fields within a page is not guaranteed. An unknown entityType silently falls back to location.

Permission: tql:fields

## TransportOrder

A transport order is a customer's request to move goods. It groups one or more consignments (goods with their load and unload actions), the customer, pricing elements and a quote. Status runs from concept or requested, through accepted, calculated_trip, partially_planned and planned, to actual and realized; it can also end as cancelled or declined. Orders created by customer-portal users always start as requested and are accepted or declined by the planner. Orders are executed by trips, either directly or split into legs that can be forwarded to carriers through capacity contracts. They can be shared through share codes for public track-and-trace, saved as and created from templates, turned into tenders, and invoiced. Types are standard, urgent, expected and forwarded; list and search endpoints return only standard orders unless asked otherwise.

### Update a transport order

 - [PUT /transportOrder](https://docs.transportial.com/apis/transportorder/update:transportorder.md): Updates the transport order identified by the id in the body. The body is merged onto the stored order: fields that are omitted keep their current value, collections that are sent (consignments, pricing elements, documents, ...) replace the stored collection. Server-owned (read-only) fields are ignored.

The order is validated and saved like on create: a missing order id or name is generated, consignments are linked to the order and inherit its type and team, and live updates are pushed to every window that shows the order.

By default the order is returned in the standard TransportOrder view; add the query flag extended to receive the extended view (ExtendedTransportOrderResponse, including trips and actions per consignment).

An id that exists on no platform is not rejected: the body is then saved as a new order.

Permission: update:transportOrder

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a transport order of another platform; 409 when versionNumber is stale because the order was changed concurrently.

### Create a transport order

 - [POST /transportOrder](https://docs.transportial.com/apis/transportorder/create:transportorder.md): Creates and saves a transport order with its consignments.

Customer- and supplier-portal accounts receive only their own side of the money: customers never get cost data (expected cost, pricing elements, incoming quotes and invoices, trip money), suppliers never get revenue data (expected revenue, the customer's quote, invoices, contract and bill-to party; only pricing elements owed to their own business remain).

The order type is always standard. When status is omitted or not a known status the order is created as accepted. team may be given as an id or object reference; an embedded customer object is resolved or created as a customer of your platform. Saving triggers the usual side effects (numbering, status evaluation, live updates, message automations and integrations). Use ?extended to receive the extended representation.

Permission: create:transportOrder

The duplicate check of planningSettings.duplicateOrderCheck runs first (see POST /transportOrders/duplicates).

Errors: 400 when the body is not valid JSON or does not match the transport order structure; 400 with transportOrder_possible_duplicate (resend with allowDuplicate=true to create it anyway) or transportOrder_duplicate_blocked when it duplicates an existing order (params.transportOrderId, orderId, name, createdAt, count).

### Preview the occurrences of a schedule

 - [POST /transportOrderSchedule/{id}/preview](https://docs.transportial.com/apis/transportorder/get:transportorder:schedulepreview.md): Lists every (date, copy) of the schedule in the range with its generation state (new, created, failed, pending) and how many orders a generation would create now. Nothing is created. The range defaults to today .. today + the effective lead time and may cover at most maxRangeDays.

Requires the platform feature orderTemplates; without it every schedule endpoint answers 404.

Permission: get:transportOrder

Errors: 400 when the order is not a template, a date is invalid, to is before from or the range is longer than maxRangeDays; 404 when no transport order with this id exists on your platform, the template has no schedule, or the features are not enabled.

### Find existing orders a new order would duplicate

 - [POST /transportOrders/duplicates](https://docs.transportial.com/apis/transportorder/check:transportorder:duplicates.md): Runs the duplicate check of planningSettings.duplicateOrderCheck for an order that is about to be created, without saving anything. An existing order (same platform, not deleted, not a template, created within lookBackDays) is a duplicate when all matchFields match; when a configured reference field is blank on the sent order nothing matches. behaviour says what POST /transportOrder will do for the caller's channel: with warn the create is refused with transportOrder_possible_duplicate until it is resent with allowDuplicate=true; with block it is refused with transportOrder_duplicate_blocked.

Permission: create:transportOrder

Errors: 400 when the body is not a valid order.

### Accept a transport order

 - [GET /transportOrder/{id}/accept](https://docs.transportial.com/apis/transportorder/accept:transportorder.md): Accepts a transport order that is still requested or concept: the order becomes accepted, every consignment is set to confirmed and the order's own quote (if any, and not yet invoiced) becomes accepted.

Orders in any other status are returned unchanged (no error). Use ?extended to receive the extended representation.

Permission: accept:transportOrder

Errors: 404 when no transport order with this id exists on your platform.

### Get the recurring schedule of an order template

 - [GET /transportOrderSchedule/{id}](https://docs.transportial.com/apis/transportorder/get:transportorder:schedule.md): Returns the recurring schedule of an order template with its next occurrence dates. schedule is null when the template has no schedule.

Requires the platform feature orderTemplates; without it every schedule endpoint answers 404.

Permission: get:transportOrder

Errors: 400 when the order is not a template; 404 when no transport order with this id exists on your platform, or the features are not enabled.

### Create or update the schedule of an order template

 - [PUT /transportOrderSchedule/{id}](https://docs.transportial.com/apis/transportorder/update:transportorder:schedule.md): Creates the template's schedule, or updates it: only the fields present in the body change. The recurrence is stored as a recurring calendar event (non-blocking, titled after the template), so it shows in the calendar and a single date can be skipped through POST /calendar/recurring-events/{recurringEventId}/exception with isCanceled.

Orders are generated leadTimeDays ahead by the master scheduler (each schedule at most every runIntervalMinutes, a platform at most maxOrdersPerRun orders per tick; a paused schedule is skipped) or on demand through POST /transportOrderSchedule/{id}/generate. A generated order is a copy of the template made through the normal create path (bill-to, credit control, order defaults, pricing). Its stop windows move onto the occurrence date: the anchor stop lands on that date and every other window keeps its day offset and wall-clock time in the platform time zone (so DST does not move an 08:00 pickup). Each template date and copy is generated once; editing the schedule or the template only affects dates not generated yet. A weekly schedule without daysOfWeek uses the weekday of validFrom; validFrom defaults to today. Saving resets nextRunAt, so the next scheduler tick applies the change.

Requires the platform feature orderTemplates; without it every schedule endpoint answers 404.

Permission: update:transportOrder

Errors: 400 when the order is not a template, or a value is invalid (interval below 1, validUntil before validFrom, rotation without weeks, copiesPerOccurrence outside 1..maxOrdersPerRun, leadTimeDays outside 0..366, generatedStatus other than accepted, concept or requested); 404 when no transport order with this id exists on your platform, or the features are not enabled.

### Delete the schedule of an order template

 - [DELETE /transportOrderSchedule/{id}](https://docs.transportial.com/apis/transportorder/delete:transportorder:schedule.md): Stops the recurrence of an order template: the schedule and its calendar event are marked deleted. Orders generated before stay, and so does the record of which dates were generated (a new schedule does not generate those dates again). Answers with schedule null.

Requires the platform feature orderTemplates; without it every schedule endpoint answers 404.

Permission: update:transportOrder

Errors: 400 when the order is not a template; 404 when no transport order with this id exists on your platform, or the features are not enabled.

### List transport orders as lean rows

 - [GET /transportOrders/list/{offset}/{amount}](https://docs.transportial.com/apis/transportorder/get:transportorders:list.md): Returns a page of transport orders as lean, scroll-optimised list rows (TransportOrderListRow): only the columns an order overview needs, plus per-order consignment counts. The heavy consignment graph (pricing, documents, full actions) is not loaded. Fetch the full order with GET /transportOrder/{id} when a row is opened.

Selection and order are the same as GET /transportOrders/{offset}/{amount}: orders of your platform that are not archived, deleted or templates, filtered by the type query parameter (default: standard only), most recently updated first. Customer-portal users only see orders whose customer is one of their businesses; supplier users only orders executed by one of their businesses.

Pagination: offset is the number of orders to skip and amount the page size; totalResults is the total number of matching orders. Query errors are logged and answered with an empty page.

Permission: get:transportOrders

Errors: 400 when offset is not an integer.

### Search transport orders as lean rows

 - [POST /transportOrders/list/{offset}/{amount}](https://docs.transportial.com/apis/transportorder/search:transportorders:list.md): Searches transport orders through the search index and returns a page of lean, scroll-optimised list rows (TransportOrderListRow) with per-order consignment counts; the heavy consignment graph is not loaded. Fetch the full order with GET /transportOrder/{id} when a row is opened. Duplicate index hits are removed, so a page never repeats a row.

The body filters with a full-text query, status/statuses, order types, consignment type/consignmentType, consignmentAttributes, customer/customers (business ids), teams, deliveryTerms, startDate (created at or after) and consignment action date ranges; filters are combined with AND. Only orders of your platform that are not archived, deleted or templates are searched. Order types are filtered by the types query parameter and/or the body types; with neither (and no body type) only standard orders match. Customer-portal users only find orders whose customer is one of their businesses, supplier users only orders executed by one of their businesses (none at all when the user has no businesses).

Sorting: sortBy is a list of { "fields": [""], "direction": "asc" | "desc" } objects (default direction desc); without sortBy results are ranked by relevance.

Pagination: offset is the number of orders to skip and amount the page size; totalResults is the total number of matches. Search errors (for example an unparsable date range) are logged and answered with an empty page.

The search is recorded as the window's last search, which decides which live updates the window receives.

Permission: get:transportOrders

Errors: 400 when offset is not an integer, or the body is missing, not valid JSON or cannot be read as a search request.

### List trips of a transport order (customer view)

 - [GET /transportOrder/{id}/trips:customer](https://docs.transportial.com/apis/transportorder/get:transportorder:trips:customer.md): Returns all trips (legs) that carry consignments of the transport order, sorted by start date, for customer-portal use.

Customer-portal users can only read orders of their own businesses, and data of other customers co-loaded on the same trips is anonymised. Each trip is cut to tracking data (status, dates, vehicle, equipment, stops, route) with no prices, costs, quotes or budget. Same behaviour as GET /transportOrder/{id}/trips. Not paginated: totalResults equals the number of trips returned.

Permission: get:transportOrder

Errors: 404 when no transport order with this id exists on your platform or you may not read it.

### List trips of a transport order

 - [GET /transportOrder/{id}/trips](https://docs.transportial.com/apis/transportorder/get:transportorder:trips.md): Returns all trips (legs) that execute a transport order: the distinct trips of all its consignments, sorted by trip start date. Use it to see how an order is planned, for example after POST /transportOrder/{id}/split.

For customer-portal users, other customers' co-loaded consignments and stops on shared trips are anonymised, and orders of businesses the user is not linked to answer 404.

The list is not paginated; totalResults equals the number of trips returned.

Permission: read:transportOrder or read:trip (either is sufficient)

Errors: 404 when no transport order with this id exists on your platform, or when it belongs to a business the (external) user is not linked to.

### Create a tender from a transport order

 - [POST /transportOrder/{id}/tender](https://docs.transportial.com/apis/transportorder/transportorder:tender.md): Creates and saves a new tender (marketplace request) based on a transport order, so the work can be offered to carriers. The tender takes the order's name and description and is issued by your platform's own business. The transport order itself is not changed.

Permission: transportOrder:tender

Errors: 404 when no transport order with this id exists on your platform, or when your platform has no business configured.

### Search transport orders (extended view)

 - [POST /transportOrders:extended/{offset}/{amount}](https://docs.transportial.com/apis/transportorder/search:transportorders:extended.md): Searches transport orders through the search index and returns a page in the extended view: each order with, per consignment, its trips and their actions. For customer-portal users, other customers' co-loaded consignments and stops on shared trips are anonymised.

Customer- and supplier-portal accounts receive only their own side of the money: customers never get cost data (expected cost, pricing elements, incoming quotes and invoices, trip money), suppliers never get revenue data (expected revenue, the customer's quote, invoices, contract and bill-to party; only pricing elements owed to their own business remain).

The body filters with a full-text query, status/statuses, order types, consignment type/consignmentType, consignmentAttributes, customer/customers (business ids), teams, deliveryTerms, startDate (created at or after) and consignment action date ranges; filters are combined with AND. Only orders of your platform that are not archived, deleted or templates are searched. Order types are filtered by the types query parameter and/or the body types; with neither (and no body type) only standard orders match. Customer-portal users only find orders whose customer is one of their businesses, supplier users only orders executed by one of their businesses (none at all when the user has no businesses).

Sorting: sortBy is a list of { "fields": [""], "direction": "asc" | "desc" } objects (default direction desc); without sortBy results are ranked by relevance.

Pagination: offset is the number of orders to skip and amount the page size; totalResults is the total number of matches. Search errors (for example an unparsable date range) are logged and answered with an empty page.

The search is recorded as the window's last search, which decides which live updates the window receives.

Permission: get:transportOrders

Errors: 400 when offset is not an integer, or the body is missing, not valid JSON or cannot be read as a search request.

### Resolve order defaults for the order form

 - [POST /transportOrderDefaults/resolve](https://docs.transportial.com/apis/transportorder/create:transportorder:defaults.md): Returns the default values the server applies when the order is created, so a form can prefill them visibly: the order-level defaults (customer over platform) and, per stop, the effective stop defaults (location over customer over platform) with its time window completed by the time-window default. Nothing is stored.

Defaults are applied server-side on every create anyway; this endpoint only previews them. A stop window with both sides set is explicit and comes back unchanged.

Permission: create:transportOrder

Errors: 400 when a date is not an ISO-8601 instant or the body is not valid JSON.

### List transport order templates

 - [POST /transportOrders/templates/{offset}/{amount}](https://docs.transportial.com/apis/transportorder/get:transportordertemplates.md): Returns a page of the platform's transport order templates (orders saved with template = true, not archived or deleted), newest first. Templates are reusable order blueprints: instantiate one with POST /transportOrder/{id}/createFromTemplate.

The optional body filters by a free-text query, matched case-insensitively as a substring of the template's name, order id or description. An empty or missing body returns all templates.

Pagination: offset is the number of templates to skip and amount the page size (not capped by the server for this endpoint). totalResults is the total number of matching templates.

Only available when the platform feature orderTemplates is enabled.

Permission: get:transportOrders

Errors: 400 when the body is not valid JSON; 404 when the orderTemplates feature is not enabled for your platform.

### Get a transport order

 - [GET /transportOrder/{id}](https://docs.transportial.com/apis/transportorder/get:transportorder.md): Returns one transport order of your platform by id, with its consignments, customer, pricing and quote, serialised as the standard TransportOrder view.

Customer- and supplier-portal accounts receive only their own side of the money: customers never get cost data (expected cost, pricing elements, incoming quotes and invoices, trip money), suppliers never get revenue data (expected revenue, the customer's quote, invoices, contract and bill-to party; only pricing elements owed to their own business remain).

Use this endpoint to open or edit an order: the list endpoints (/transportOrders/list/...) return lean rows only. For the view that also carries the trips and actions of every consignment, use GET /transportOrder:extended/{id}.

Customer-portal and supplier users can only read orders of their own businesses; any other order answers 404 so the existence of another customer's order is not revealed.

Permission: get:transportOrder

Errors: 404 when no transport order with this id exists on your platform, or when it belongs to a business the (external) user is not linked to.

### Delete a transport order

 - [DELETE /transportOrder/{id}](https://docs.transportial.com/apis/transportorder/delete:transportorder.md): Soft-deletes a transport order: the order and all its consignments are flagged as deleted and force-cancelled regardless of their current status (consignments cancelled, order status cancelled, activity logged). Deleted orders no longer appear in lists or searches.

Permission: delete:transportOrder

Errors: 404 when no transport order with this id exists on your platform.

### Cancel a transport order

 - [GET /transportOrder/{id}/cancel](https://docs.transportial.com/apis/transportorder/cancel:transportorder.md): Cancels a transport order that is requested or accepted: every consignment is cancelled, the order status becomes cancelled, the order's own quote (if any, and not yet invoiced) becomes cancelled, and a "Transport order cancelled" activity is logged.

Orders in any other status are returned unchanged (no error).

Permission: accept:transportOrder

Errors: 404 when no transport order with this id exists on your platform.

### Release a transport order from hold

 - [POST /transportOrder/{id}/release](https://docs.transportial.com/apis/transportorder/release:transportorder.md): Releases the transport order from hold: hold.onHold becomes false and the outbound events recorded while it was held are raised again with its current state. Consignments and trips that were held because of this order are released with it; items held on their own stay on hold. Fires afterReleasingTransportOrder.

Permission: release:transportOrder

Errors: 400 with hold_not_on_hold; 404 when no transport order with this id exists on your platform.

### Get a transport order (extended)

 - [GET /transportOrder:extended/{id}](https://docs.transportial.com/apis/transportorder/get:transportorder:extended.md): Returns one transport order in its extended form, including its consignments with their trips and actions.

Customer-portal users can only read orders of their own businesses, and data of other customers co-loaded on the same trips is anonymised.

Permission: get:transportOrder

Errors: 404 when no transport order with this id exists on your platform or you may not read it.

### List transport orders with a status

 - [GET /transportOrders/{status}/{offset}/{amount}](https://docs.transportial.com/apis/transportorder/get:transportorders:status.md): Returns a page of transport orders that currently have the given status, in the standard TransportOrder view, most recently updated first.

Only orders of your platform that are not archived, deleted or templates are listed, filtered by the type query parameter (default: standard orders only). Customer-portal users only see orders whose customer is one of their businesses; supplier users only orders executed by one of their businesses. Use GET /transportOrders:extended/{status}/{offset}/{amount} for the extended view.

Pagination: offset is the number of orders to skip and amount the page size; totalResults is the total number of matching orders.

The request is recorded as the window's last search, which decides which live updates the window receives. Query errors are logged and answered with an empty page.

Permission: get:transportOrders

Errors: 400 when offset is not an integer.

### List transport orders with a status (extended)

 - [GET /transportOrders:extended/{status}/{offset}/{amount}](https://docs.transportial.com/apis/transportorder/get:transportorders:extended:status.md): Returns a page of non-archived, non-deleted, non-template transport orders with the given status, in their extended form (consignments with trips and actions), newest update first, plus the total count.

status is matched case-insensitively against declined, cancelled, requested, accepted, calculated_trip, partially_planned, planned, actual, realized, concept; an unknown value falls back to requested. Only standard orders are returned unless one or more type query parameters are given. Customer and supplier accounts only see orders of their own businesses (as customer or executing business respectively).

Permission: get:transportOrders

Errors: 400 when offset is not an integer.

### Put a transport order on hold

 - [POST /transportOrder/{id}/hold](https://docs.transportial.com/apis/transportorder/hold:transportorder.md): Puts the transport order on hold (do-not-dispatch): hold.onHold becomes true with the reason, the user and the time. While held, nothing about it is sent to drivers, board computers, integrations, message automations, workflows, app webhooks or carriers; those outbound events are recorded and raised again on release. A held trip cannot be confirmed, started or offered to drivers, and a held order cannot be accepted. Cancellations still go out. Its consignments are put on hold with it, and the trips carrying them as planningSettings.holdSettings.tripCascade says (default exclusive_trips: trips carrying nothing but this order's goods); started trips are never held. Fires afterHoldingTransportOrder.

Permission: hold:transportOrder

Errors: 400 with hold_already_on_hold, hold_reason_required (a reason is required by the settings); 404 when no transport order with this id exists on your platform.

### Create a transport order from a template

 - [POST /transportOrder/{id}/createFromTemplate](https://docs.transportial.com/apis/transportorder/createfromtemplate:transportorder.md): Instantiates a real, plannable transport order from an order template and saves it.

The new order is a deep copy of the template with fresh ids, numbers, statuses and times; identifiers, documents, invoices, administration, expected revenue/costs, reference description and name are not copied. Pricing elements and the quote lines are copied onto the new order. The customer is kept. No request body is read.

Requires the platform feature orderTemplates.

Permission: create:transportOrder

Errors: 400 when the order with this id is not a template; 404 when no transport order with this id exists on your platform, or when the orderTemplates feature is not enabled for your platform.

### Create an invoice from a transport order

 - [POST /transportOrder/{id}/create:invoice](https://docs.transportial.com/apis/transportorder/create:invoicefromtransportorder.md): Creates and saves a new invoice for the transport order and links it to the order's invoices. The invoice takes the order's administration and team, uses the order name as reference and the order's reference description as description, invoices the customer's business and is issued by the executing business (or the administration's business when none is set).

No request body is read.

Permission: create:invoice

Errors: 404 when no transport order with this id exists on your platform.

### List the orders generated from a template

 - [GET /transportOrderSchedule/{id}/generated/{offset}/{amount}](https://docs.transportial.com/apis/transportorder/get:transportorders:generated.md): Lists the generated occurrences of an order template, newest occurrence date first, with the order each one created (traceability from template to orders), its state and any error.

Requires the platform feature orderTemplates; without it every schedule endpoint answers 404.

Permission: get:transportOrders

Errors: 400 when the order is not a template; 404 when no transport order with this id exists on your platform, or the features are not enabled.

### List trips of a shared transport order

 - [GET /transportOrder/{id}/{shareCode}/trips](https://docs.transportial.com/apis/transportorder/get:transportorder:public:trips.md): Returns the trips that execute a shared transport order (all distinct trips of its consignments), for public track-and-trace pages. No user session is needed; the share code authorises the request when the order's share settings are open and the code matches exactly.

The order's share settings control what is exposed: when trip tracking is not shared the list is empty; chauffeurs, the vehicle and documents are stripped from every trip unless sharing them is enabled. The list is not paginated; totalResults equals the number of trips returned.

Financial privacy: the trip keeps its shape but carries no money fields (expectedCost, expectedRevenue, actualCost, pricingElements, quotes, invoices, route cost and expenses, ledgerEntries, lockedBudget); capacityContract is cut to the carrier's identity.

Permission: Public, no authentication (the share code authorises the request).

Errors: 401 when sharing is not enabled for the order, the order has no share code, the share link has expired (validUntil) or the share code does not match; 404 when no transport order with this id exists.

### List transport orders

 - [GET /transportOrders/{offset}/{amount}](https://docs.transportial.com/apis/transportorder/get:transportorders.md): Returns a page of transport orders in the standard TransportOrder view, most recently updated first.

Only orders of your platform that are not archived, deleted or templates are listed, filtered by the type query parameter (default: standard orders only). Customer-portal users only see orders whose customer is one of their businesses; supplier users only orders executed by one of their businesses.

Pagination: offset is the number of orders to skip and amount the page size; totalResults is the total number of matching orders. For scroll-optimised lean rows use GET /transportOrders/list/{offset}/{amount}; to filter, use POST /transportOrders/{offset}/{amount}.

The request is recorded as the window's last search, which decides which live updates the window receives. Query errors are logged and answered with an empty page.

Permission: get:transportOrders

Errors: 400 when offset is not an integer.

### Search transport orders

 - [POST /transportOrders/{offset}/{amount}](https://docs.transportial.com/apis/transportorder/search:transportorders.md): Searches transport orders through the search index and returns a page in the standard TransportOrder view.

The body filters with a full-text query, status/statuses, order types, consignment type/consignmentType, consignmentAttributes, customer/customers (business ids), teams, deliveryTerms, startDate (created at or after) and consignment action date ranges; filters are combined with AND. Only orders of your platform that are not archived, deleted or templates are searched. Order types are filtered by the types query parameter and/or the body types; with neither (and no body type) only standard orders match. Customer-portal users only find orders whose customer is one of their businesses, supplier users only orders executed by one of their businesses (none at all when the user has no businesses).

Sorting: sortBy is a list of { "fields": [""], "direction": "asc" | "desc" } objects (default direction desc); without sortBy results are ranked by relevance.

Pagination: offset is the number of orders to skip and amount the page size; totalResults is the total number of matches. Search errors (for example an unparsable date range) are logged and answered with an empty page.

The search is recorded as the window's last search, which decides which live updates the window receives.

Permission: get:transportOrders

Errors: 400 when offset is not an integer, or the body is missing, not valid JSON or cannot be read as a search request.

### List the incidents of a transport order

 - [GET /transportOrder/{id}/incidents](https://docs.transportial.com/apis/transportorder/get:transportorder:incidents.md): The incidents concerning the order's consignments as timeline entries (type, time, follow-up state), oldest first. Customer-portal users only read orders of their own businesses and only receive customer-visible incidents. Empty when the incidents feature is off.

Requires the incidents feature on your platform.

Permission: get:transportOrder

Errors: 404 when the order does not exist on your platform or is not yours (portal).

### Get a shared transport order (extended view)

 - [GET /transportOrder:extended/{id}/{shareCode}](https://docs.transportial.com/apis/transportorder/get:transportorder:public:extended.md): Returns a transport order that was shared through a share link in the extended view: the order plus, per consignment, its trips and their actions. No user session is needed; the share code authorises the request when the order's share settings are open and the code matches exactly.

On success the request is re-scoped to the platform that owns the order.

Financial privacy: the holder of the link is treated as the order's customer: no cost side (expectedCost, actualCost, pricingElements, capacityContract), no ledgerEntries, lockedBudget, pricingModules, creditLimit or creditStatus, no incoming quotes or invoices and no charges awaiting internal approval. Trips keep their shape but carry no money fields.

Permission: Public, no authentication (the share code authorises the request).

Errors: 401 when sharing is not enabled for the order, the order has no share code, the share link has expired (validUntil) or the share code does not match; 404 when no transport order with this id exists.

### Generate the orders of a schedule for a range

 - [POST /transportOrderSchedule/{id}/generate](https://docs.transportial.com/apis/transportorder/create:transportorder:schedule.md): Creates the missing orders of the schedule in the range now, also for a paused schedule, at most maxOrdersPerRun (capped tells whether more remain; run again for them). Dates generated before are skipped; with retryFailed the failed ones are tried again. Each order is committed on its own, so one refused order (for example by credit control) does not undo the others; its error is reported per item and kept on the occurrence.

Requires the platform feature orderTemplates; without it every schedule endpoint answers 404.

Permission: create:transportOrder

Errors: 400 when the order is not a template, a date is invalid, to is before from or the range is longer than maxRangeDays; 404 when no transport order with this id exists on your platform, the template has no schedule, or the features are not enabled.

### Receive an order e-mail (inbound mail webhook)

 - [PUT /transportOrder/mail/receiver](https://docs.transportial.com/apis/transportorder/create:transportorder:frommail.md): Inbound e-mail webhook: turns an e-mail sent to a platform's order address into transport orders.

The platform is the local part of the first transportial.com recipient in the multipart envelope field. The sender's domain is validated against the platform's allowed e-mail domains and the attachments are stored as files. Processing then continues asynchronously: attachments matching a file-template integration are mapped to orders, otherwise the platform's AI model extracts an order from the e-mail; existing orders matched by reference (as for POST /transportOrder/from-documents, limited to the extracted customer when it is an existing business) are updated, new ones saved, and the original e-mail is posted into each order's consignment chat. The response only confirms that processing started.

Permission: Public, no authentication (called by the mail provider).

Errors: 400 when the platform cannot be found, the sender address is missing or malformed, or the sender's domain is not allowed for the platform.

### Decline a transport order

 - [GET /transportOrder/{id}/decline](https://docs.transportial.com/apis/transportorder/decline:transportorder.md): Declines a transport order that is still requested or concept: the order becomes declined, every consignment is set to cancelled, any tender of a consignment is closed and deleted, and the order's own quote (if any, and not yet invoiced) becomes declined.

Orders in any other status are returned unchanged (no error).

Permission: decline:transportOrder

Errors: 404 when no transport order with this id exists on your platform.

### Get a shared transport order by share code

 - [GET /transportOrder/{id}/{shareCode}](https://docs.transportial.com/apis/transportorder/get:transportorder:public.md): Returns a transport order that was shared through a share link, without a user session. Access is granted by the share code instead of a login: the order's share settings must be open and the code must match exactly.

On success the request is re-scoped to the platform that owns the order, and the order is returned in the standard TransportOrder view. Use GET /transportOrder:extended/{id}/{shareCode} for the view including trips and actions, and GET /transportOrder/{id}/{shareCode}/trips for the shared trip tracking.

Financial privacy: the holder of the link is treated as the order's customer: no cost side (expectedCost, actualCost, pricingElements, capacityContract), no ledgerEntries, lockedBudget, pricingModules, creditLimit or creditStatus, no incoming quotes or invoices and no charges awaiting internal approval. Trips keep their shape but carry no money fields.

Permission: Public, no authentication (the share code authorises the request).

Errors: 401 when sharing is not enabled for the order, the order has no share code, the share link has expired (validUntil) or the share code does not match; 404 when no transport order with this id exists.

### Get an unsaved copy of a transport order

 - [GET /transportOrder/{id}/duplicate](https://docs.transportial.com/apis/transportorder/duplicate:transportorder.md): Returns an unsaved copy of the transport order to seed a create form; nothing is written. Submitting that form to POST /transportOrder creates the order.

The copy has no id, uuid, numbers, statuses or times; identifiers (at every level), documents, invoices, administration, expected revenue/costs, reference description and name are dropped. The customer, pricing elements and quote lines are copied.

Permission: duplicate:transportOrder

Errors: 404 when no transport order with this id exists on your platform.

### List administration activity of a transport order

 - [GET /transportOrder/{id}/activity](https://docs.transportial.com/apis/transportorder/get:transportorder:activity.md): Returns the administration activity timeline of a transport order: every non-deleted administration activity record linked to the order (name, type, description, activity type), newest first, with the name and id of the user who caused it.

Each entry carries its field-level changes (old and new value per field), the part it happened on (subjectType, subjectLabel, e.g. a stop or a line) and its origin (source: user, API, integration or system). Paginated with the offset and amount query parameters; totalResults is the total number of entries.

Permission: get:transportOrder

Errors: 404 when no transport order with this id exists on your platform, or (for customer-portal users) the order does not belong to one of your businesses.

### Create transport orders from documents

 - [POST /transportOrder/from-documents](https://docs.transportial.com/apis/transportorder/create:transportorder:fromdocument.md): Parses uploaded files into transport orders.

documents holds ids of files already uploaded to your platform (unknown ids are ignored). For each file the server fingerprints its contents and looks for a matching file-template integration:
- Template matched: the template's field mappings turn the file into one or more transport orders, which are returned.
- No template matched: the platform's AI model extracts a transport order from the files (and the optional html text in the body). If an existing order matches it by reference that order is updated; otherwise a new order is saved with the files attached as documents. Matching: the same id or uuid first, else the order number, reference description and identifiers (case-insensitive; an order-number match outranks a reference description or identifier match, equal matches go to the newest order). When the extracted customer is an existing business, only orders of that customer (or without a customer) are matched.

Permission: create:transportOrder

Errors: 400 when the body is not valid JSON.

### Save a transport order as a template

 - [POST /transportOrder/{id}/saveAsTemplate](https://docs.transportial.com/apis/transportorder/saveastemplate:transportorder.md): Copies an existing transport order into a new, persisted order template that can later be instantiated with POST /transportOrder/{id}/createFromTemplate.

The copy keeps the consignments, goods, actions, customer, pricing elements and quote lines of the source, but drops everything that belongs to the source only: ids, order/consignment/trip numbers, statuses and life cycles, start/end times, documents, invoices, administration, expected revenue and costs, external identifiers and execution results. A new order id is generated on save.

The template is named after the optional name in the body, falling back to the source order's name. The source order is not changed.

Only available when the platform feature orderTemplates is enabled.

Permission: create:transportOrder

Errors: 400 when the body is not valid JSON; 404 when the orderTemplates feature is not enabled for your platform, or when no transport order with this id exists on your platform.

### Cancel several transport orders

 - [POST /transportOrders/cancel](https://docs.transportial.com/apis/transportorder/cancel:transportorders.md): Cancels every transport order in ids that is requested or accepted (same behaviour as cancelling a single order: consignments cancelled, status cancelled, activity logged). Each order is committed in its own transaction.

Ids that do not resolve to a transport order on your platform are silently skipped; orders in another status are returned unchanged.

Permission: accept:transportOrder

Errors: 400 when the body is not valid JSON or has no ids array.

### Bulk-edit transport orders

 - [POST /transportOrders/bulkEdit](https://docs.transportial.com/apis/transportorder/update:transportorders:bulkedit.md): Applies one or more field operations to every transport order in ids.

Supported field keys: actionTimeWindow, status, customer, executingBusiness, team and administration (assignment fields take the target entity id as value; customer takes a business id and wraps it in a new customer). Every operation is validated before any order is touched. Each order is then edited and committed in its own transaction, so one failing order does not fail the batch: per-order outcomes are returned in results, and transportOrders/totalResults contain only the orders that were updated.

Gated by the platform's bulk-edit settings (bulkEditEnabled, per-field toggles and bulkEditMaxOrders, default 500).

Permission: update:transportOrder

Errors: 400 when bulk-edit is disabled for the platform, when ids exceeds bulkEditMaxOrders, when an operation names an unknown field or a field disabled for the platform, or when the body is not valid JSON. Orders that are not found or fail to update are reported in results rather than as an error.

### Forward a transport order to a carrier

 - [POST /transportOrder/{id}/forward](https://docs.transportial.com/apis/transportorder/forward:transportorder.md): Forwards the entire transport order to one external carrier: creates a single forwarded trip covering all consignments, assigned to the given capacity contract. When the order is accepted it becomes partially_planned.

For several legs or carriers use POST /transportOrder/{id}/split instead.

Permission: update:transportOrder and create:trip

Errors: 400 when capacityContractId is missing or blank, or the order has no consignments; 404 when no transport order with this id exists on your platform.

### Split a transport order into trips (legs)

 - [POST /transportOrder/{id}/split](https://docs.transportial.com/apis/transportorder/split:transportorder.md): Creates one trip per leg definition to execute a transport order in segments, for example pickup by one carrier, line-haul by a second and delivery by a third. Each leg can be outsourced to an external carrier through a capacity contract.

Per leg the server:
- takes the listed consignments (all consignments of the order when consignmentIds is empty);
- builds the route from stopLocationIds when at least two are given, otherwise from the leg's origin/destination (falling back to the consignments' own pickup and delivery locations); a stop already claimed by an earlier leg gets a fresh trip-only stop on later legs;
- assigns the optional vehicle and chauffeurs;
- attaches the referenced order/consignment pricing elements plus newly defined ones. With a capacity contract, costs billed by that carrier become lines on a new carrier spot-quote together with the explicit quoteLines, instead of trip pricing elements;
- saves the trip with status accepted, type forwarded when a capacity contract is given and internal otherwise, named after name or its origin and destination.

An order with status accepted moves to partially_planned. The trips are committed before the response is sent.

Permission: update:transportOrder or create:trip (either is sufficient)

Errors: 400 when the body is not valid JSON, legs is empty, or a leg matches none of the order's consignments; 404 when no transport order with this id exists on your platform.

## Consignment

A consignment is a set of goods (items and transport equipment) that travels together from pickup to delivery under one set of instructions. It belongs to a transport order, carries its own stops (actions) with time windows, documents, actors and expected cost/revenue, and is executed by one or more trips. The server generates its consignment number and chat on save, and derives its status (confirmed, partially_planned, planned, in_transit, completed, requires_replanning, ...) from the lifecycles of its actions. Consignments can be combined into a main consignment and split again; lightweight list rows serve the Watchtower and an extended view adds the executing trips and chat.

### Cancel a booked shipment or stop following a tracking number

 - [DELETE /consignment/{id}/parcel/{integrationId}/{trackingNumber}](https://docs.transportial.com/apis/consignment/update:consignment:parcel:remove.md): For a shipment booked through Transportial: cancels it at the carrier, which is only possible before pickup, and keeps it in the list as cancelled. For a tracking number added by hand, or a shipment already cancelled, delivered or returned: stops following it and removes it from the list without calling the carrier.

Permission: update:consignment

Errors: 400 parcel_integration_not_found when the integration is not a parcel carrier integration of your platform, parcel_shipment_not_found when the consignment has no such shipment, parcel_shipment_not_cancellable when the carrier already picked it up, parcel_carrier_configuration when the integration's settings are wrong; 502 parcel_carrier_error when the carrier refused the cancellation; 404 when no live consignment with this id exists on your platform.

### List a consignment's parcel shipments and the carriers that can book them

 - [GET /consignment/{id}/parcel](https://docs.transportial.com/apis/consignment/get:consignment:parcel.md): Returns the parcel/express/freight carrier integrations of your platform (DHL Express, DHL Parcel NL/DE, DHL Freight, UPS, FedEx) that are enabled, with their services and default service, and the consignment's parcel shipments: booked through an integration or followed by a tracking number entered by hand, with the latest carrier status and events.

Permission: get:consignment

Errors: 404 when no live consignment with this id exists on your platform.

### Get a consignment (extended view)

 - [GET /consignment:extended/{id}](https://docs.transportial.com/apis/consignment/get:consignment:extended.md): Returns one consignment of your platform serialised for the extended (detail) view: like the standard view, plus the trips executing the consignment and its chat, but without identifiers. This is the call the planner makes when a consignment row is opened.

Permission: get:consignments

Errors: 404 when no consignment with this id exists on your platform (also for archived or deleted consignments).

### Book a carrier shipment for a consignment

 - [POST /consignment/{id}/parcel/book](https://docs.transportial.com/apis/consignment/update:consignment:parcel:book.md): Creates the shipment at the carrier of the given integration and stores its tracking number and labels on the consignment (labels as consignment documents). Shipper is the first loading address, receiver the last delivery address; pieces come from the goods (weights in kg, dimensions in cm); customs data is sent for international shipments. Calling it again while the consignment still has an open shipment with this integration books nothing and returns the shipments as they are.

Permission: update:consignment

Errors: 400 parcel_integration_not_found when the integration is not an enabled parcel carrier integration of your platform, parcel_shipment_incomplete (with params.missing: shipperAddress, receiverAddress, weight) when the consignment lacks what the carrier needs, parcel_carrier_configuration when the integration's credentials or settings are wrong; 502 parcel_carrier_error when the carrier refused or failed the booking (its message in errors); 404 when no live consignment with this id exists on your platform.

### Get the tracking link of a consignment

 - [GET /consignment/{id}/tracking-link](https://docs.transportial.com/apis/consignment/get:consignment:trackinglink.md): One public tracking link per consignment, showing its stops with current ETAs, a map limited to the consignment's own legs, a status timeline and (when allowed) the proof of delivery. Returns the consignment's link with its URL, expiry and visible fields. Reading never creates a link: without one, id and url are blank and fields holds the platform default; create it with the regenerate endpoint.

Permission: get:consignment

Errors: 404 when the consignment does not exist on your platform.

### Change the fields or expiry of a tracking link

 - [PUT /consignment/{id}/tracking-link](https://docs.transportial.com/apis/consignment/update:consignment:trackinglink.md): One public tracking link per consignment, showing its stops with current ETAs, a map limited to the consignment's own legs, a status timeline and (when allowed) the proof of delivery. Sets the fields the public page shows (null = platform default) and the link's own expiry (null = the platform's days after delivery). Creates the link when there is none yet.

Permission: share:consignment

Errors: 404 when the consignment does not exist on your platform.

### Public tracking page of a consignment

 - [GET /consignment:tracking/{id}/{shareCode}](https://docs.transportial.com/apis/consignment/get:consignment:tracking:public.md): One public tracking link per consignment, showing its stops with current ETAs, a map limited to the consignment's own legs, a status timeline and (when allowed) the proof of delivery. Returns only what the link shows: stops (address and coordinates only when shown), current ETAs of open stops, the polylines of the consignment's own legs, the vehicle position while the goods are on board (never the vehicle or driver), the status timeline and, after delivery, proof-of-delivery downloads. Prices, ids, parties and other consignments are never included.

Permission: Public, no authentication; the share code authorises the request.

Errors: 401 when the link does not exist, is revoked or expired, or the code does not match.

### Split combined consignments

 - [POST /consignments/split](https://docs.transportial.com/apis/consignment/split:consignment.md): Splits a previously combined consignment back into separate consignments. The body carries the main consignment and the related consignments as they should be stored after the split; each is saved (created when it has no id yet, updated otherwise).

Every consignment in the body that already exists must belong to your platform. The response lists the main consignment first, followed by the related ones.

Permission: split:consignment

Errors: 400 when the body is not valid JSON; 404 when a consignment id in the body exists but not on your platform.

### Revoke the tracking link of a consignment

 - [POST /consignment/{id}/tracking-link/revoke](https://docs.transportial.com/apis/consignment/revoke:consignment:trackinglink.md): One public tracking link per consignment, showing its stops with current ETAs, a map limited to the consignment's own legs, a status timeline and (when allowed) the proof of delivery. Closes the link: its URL stops working at once (the public page answers 401). Regenerate to share it again with a new URL.

Permission: share:consignment

Errors: 404 when the consignment or its link does not exist.

### Add documents to a consignment

 - [PUT /consignment/{id}/documents](https://docs.transportial.com/apis/consignment/update:consignment:documents.md): Appends the given documents to the consignment's documents and saves the consignment. Existing documents are kept; documents are referenced by id (or sent in full) as in a DocumentAssociation.

Permission: update:consignment

Errors: 400 when the body is not valid JSON; 404 when no consignment with this id exists on your platform.

### List the activity timeline of a consignment

 - [GET /consignment/{id}/activity](https://docs.transportial.com/apis/consignment/get:consignment:activity.md): Returns the activity timeline of a consignment, newest first: its creation, field-level changes (old and new value per field), moves between transport orders, deletion, and changes to its stops — with who or what caused each entry (source, userName, sourceName).

Paginated with the offset and amount query parameters; totalResults is the total number of entries.

Permission: get:consignment

Errors: 404 when no consignment with this id exists on your platform, or (for customer-portal users) its transport order does not belong to one of your businesses.

### Update a consignment

 - [PUT /consignment](https://docs.transportial.com/apis/consignment/update:consignment.md): Updates the consignment identified by the id in the body: the body is merged onto the stored consignment (fields left out keep their value, collections that are sent replace the stored ones) and the consignment is saved with the same side effects as a create (consignment number, chat, action linking).

An id that does not exist yet creates a new consignment with that id.

Permission: update:consignment

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a consignment of another platform.

### Create a consignment

 - [POST /consignment](https://docs.transportial.com/apis/consignment/create:consignment.md): Creates a consignment from the body, including its nested goods, actions and other associations.

On save the server generates consignmentNr when it is blank (-C), uses it as name when no name is given, creates the consignment's public chat, links every action of the consignment and of its (contained) goods to the consignment, and gives sub-actions the location of their stop. A body whose id belongs to another platform's consignment is rejected.

Permission: create:consignment or update:consignment

Errors: 400 when the body is not valid JSON or cannot be mapped onto a consignment; 404 when the body carries the id of a consignment owned by another platform.

### Get a consignment

 - [GET /consignment/{id}](https://docs.transportial.com/apis/consignment/get:consignment.md): Returns one consignment of your platform in the standard serialisation: its order, goods, actions, documents, actors, tender, identifiers and next/last action. The trips executing it and its chat are not included; use GET /consignment:extended/{id} for those.

Permission: get:consignment

Errors: 404 when no consignment with this id exists on your platform (also for archived or deleted consignments).

### Delete a consignment

 - [DELETE /consignment/{id}](https://docs.transportial.com/apis/consignment/delete:consignment.md): Permanently removes the consignment (a hard delete, not archiving) and returns it as it was. Failures while removing are logged and do not turn into an error response.

Permission: delete:consignment

Errors: 404 when no consignment with this id exists on your platform.

### List consignments

 - [GET /consignments/{offset}/{amount}](https://docs.transportial.com/apis/consignment/get:consignments.md): Returns a page of the consignments of your platform (archived and deleted ones excluded) in the standard consignment serialisation, plus the total count. Use the search endpoint (POST on the same path) to filter and sort.

The request is recorded in the user's search history.

Permission: get:consignments

Errors: 400 when offset or amount is not an integer.

### Search consignments

 - [POST /consignments/{offset}/{amount}](https://docs.transportial.com/apis/consignment/search:consignments.md): Searches the consignments of your platform in the search index and returns one page in the standard consignment serialisation, plus the total number of matches.

The body is a search object; all keys are optional:
- query: free-text search over the consignment's search string;
- status (one value) or statuses (array): consignment statuses such as planned or in_transit (case-insensitive);
- teams: team ids of the consignment; customer / customers: business id(s) of the order's customer (or the customer's customer); attributes: attribute (label) ids, matching consignments carrying any of them; administration: administration id of the order;
- currentLocationRadius, nextActionLocationRadius, lastActionLocationRadius: { "location": { "id": "..." }, "radius": 25, "unit": "km" } limits to consignments within the radius of that location;
- nextAction.constraints.startDateTime, nextAction.constraints.endDateTime, lastAction.constraints.startDateTime, lastAction.constraints.endDateTime: { "start": ISO-8601, "end": ISO-8601 } time window on the next/last action;
- createdAt, updatedAt: { "start": ISO-8601, "end": ISO-8601 } (either bound may be left out);
- corridor: keeps only consignments along a route corridor (applied after the index search);
- sortBy: [{ "fields": ["updatedAt"], "direction": "asc" }]; without it results are ordered by relevance.

Only standard orders are searched (not quotes or other order types). External users (customers and suppliers) only see consignments of the businesses they are linked to; an external user without businesses sees nothing. Archived and deleted consignments are excluded. The request is recorded in the user's search history.

Permission: get:consignments

Errors: 400 when the body is not valid JSON, a date is not ISO-8601 or offset/amount is not an integer.

### Put a consignment on hold

 - [POST /consignment/{id}/hold](https://docs.transportial.com/apis/consignment/hold:consignment.md): Puts the consignment on hold (do-not-dispatch): hold.onHold becomes true with the reason, the user and the time. While held, nothing about it is sent to drivers, board computers, integrations, message automations, workflows, app webhooks or carriers; those outbound events are recorded and raised again on release. A held trip cannot be confirmed, started or offered to drivers, and a held order cannot be accepted. Cancellations still go out. The trips carrying it follow as planningSettings.holdSettings.tripCascade says (default exclusive_trips); started trips are never held. Fires afterHoldingConsignment.

Permission: hold:consignment

Errors: 400 with hold_already_on_hold, hold_reason_required (a reason is required by the settings); 404 when no consignment with this id exists on your platform.

### Combine consignments

 - [POST /consignments/combine](https://docs.transportial.com/apis/consignment/combine:consignment.md): Combines several consignments into one. The body is an array of consignments: the first becomes the main consignment and is saved as sent; every following one is marked combined = true, linked to the main consignment (its consignment field) and saved.

The response holds the main consignment and the consignments that were combined into it.

Permission: create:consignment or update:consignment

Errors: 400 when the body is not a JSON array of consignments; 404 when a consignment id in the body belongs to another platform.

### List consignment rows

 - [GET /consignments/list/{offset}/{amount}](https://docs.transportial.com/apis/consignment/get:consignments:list.md): Returns a page of lightweight, scroll-optimised consignment rows for the Watchtower and consignment lists, newest change first (updatedAt descending), plus the total count of consignments on your platform. Archived and deleted consignments are excluded. External users (customers and suppliers) only get consignments of the businesses they are linked to (a supplier by the order's executing business, a customer by the order's customer or the customer's customer), and the total counts only those; an external user without businesses gets an empty list.

Each row is a lean projection (ConsignmentRowModel) holding only what the list renders: the consignment's own columns, its order and customer, goods (with contained goods), its own stops with next/last action pointers, attributes and, per linked trip, the plan board's trip row. Open a consignment with GET /consignment:extended/{id} for the full object.

The request is recorded in the user's search history. A missing or non-numeric offset/amount falls back to 0 and 20.

Permission: get:consignments

### Search consignment rows

 - [POST /consignments/list/{offset}/{amount}](https://docs.transportial.com/apis/consignment/search:consignments:list.md): Searches consignments like POST /consignments/{offset}/{amount} but returns the matches as lightweight list rows, in ranking order, plus the total number of matches.

Each row is a lean projection (ConsignmentRowModel) holding only what the list renders: the consignment's own columns, its order and customer, goods (with contained goods), its own stops with next/last action pointers, attributes and, per linked trip, the plan board's trip row. Open a consignment with GET /consignment:extended/{id} for the full object.

The body is a search object; all keys are optional:
- query: free-text search over the consignment's search string;
- status (one value) or statuses (array): consignment statuses such as planned or in_transit (case-insensitive);
- teams: team ids of the consignment; customer / customers: business id(s) of the order's customer (or the customer's customer); attributes: attribute (label) ids, matching consignments carrying any of them; administration: administration id of the order;
- currentLocationRadius, nextActionLocationRadius, lastActionLocationRadius: { "location": { "id": "..." }, "radius": 25, "unit": "km" } limits to consignments within the radius of that location;
- nextAction.constraints.startDateTime, nextAction.constraints.endDateTime, lastAction.constraints.startDateTime, lastAction.constraints.endDateTime: { "start": ISO-8601, "end": ISO-8601 } time window on the next/last action;
- createdAt, updatedAt: { "start": ISO-8601, "end": ISO-8601 } (either bound may be left out);
- corridor: keeps only consignments along a route corridor (applied after the index search);
- sortBy: [{ "fields": ["updatedAt"], "direction": "asc" }]; without it results are ordered by relevance.

Only standard orders are searched (not quotes or other order types). External users (customers and suppliers) only see consignments of the businesses they are linked to; an external user without businesses sees nothing. Archived and deleted consignments are excluded. The request is recorded in the user's search history.

Permission: get:consignments

Errors: 400 when the body is not valid JSON, a date is not ISO-8601 or offset/amount is not an integer.

### Create or regenerate the tracking link of a consignment

 - [POST /consignment/{id}/tracking-link/regenerate](https://docs.transportial.com/apis/consignment/regenerate:consignment:trackinglink.md): One public tracking link per consignment, showing its stops with current ETAs, a map limited to the consignment's own legs, a status timeline and (when allowed) the proof of delivery. Creates the link, or gives an existing one a new share code: every copy of the old URL stops working and the link is open again. Queues afterSharingConsignmentTracking, whose payload is the consignment plus trackingUrl, so a configured message automation can send the new link.

Permission: share:consignment

Errors: 404 when the consignment does not exist on your platform.

### Follow a shipment booked elsewhere by its tracking number

 - [POST /consignment/{id}/parcel/track](https://docs.transportial.com/apis/consignment/update:consignment:parcel:track.md): Adds a tracking number of a shipment booked outside Transportial to the consignment. The integration's tracking handler then follows it like a booked shipment (status, events, pickup and delivery stops, proof of delivery). The number is tracked once right away; when that fails the number is still added. Adding a number the consignment already follows changes nothing.

Permission: update:consignment

Errors: 400 parcel_integration_not_found when the integration is not an enabled parcel carrier integration of your platform, parcel_tracking_number_required / parcel_tracking_number_invalid for a blank or malformed number, parcel_carrier_configuration when the integration's credentials or settings are wrong; 404 when no live consignment with this id exists on your platform.

### List consignments (extended view)

 - [GET /consignments:extended/{offset}/{amount}](https://docs.transportial.com/apis/consignment/get:consignments:extended.md): Returns a page of the consignments of your platform (archived and deleted ones excluded) serialised for the extended view, plus the total count. Same paging as GET /consignments/{offset}/{amount}; only the serialisation differs (each consignment also carries its trips and chat, but not its identifiers).

The request is recorded in the user's search history.

Permission: get:consignments

Errors: 400 when offset or amount is not an integer.

### Search consignments (extended view)

 - [POST /consignments:extended/{offset}/{amount}](https://docs.transportial.com/apis/consignment/search:consignments:extended.md): Same search as POST /consignments/{offset}/{amount}, but the consignments are serialised for the extended view (each also carries its trips and chat, but not its identifiers).

The body is a search object; all keys are optional:
- query: free-text search over the consignment's search string;
- status (one value) or statuses (array): consignment statuses such as planned or in_transit (case-insensitive);
- teams: team ids of the consignment; customer / customers: business id(s) of the order's customer (or the customer's customer); attributes: attribute (label) ids, matching consignments carrying any of them; administration: administration id of the order;
- currentLocationRadius, nextActionLocationRadius, lastActionLocationRadius: { "location": { "id": "..." }, "radius": 25, "unit": "km" } limits to consignments within the radius of that location;
- nextAction.constraints.startDateTime, nextAction.constraints.endDateTime, lastAction.constraints.startDateTime, lastAction.constraints.endDateTime: { "start": ISO-8601, "end": ISO-8601 } time window on the next/last action;
- createdAt, updatedAt: { "start": ISO-8601, "end": ISO-8601 } (either bound may be left out);
- corridor: keeps only consignments along a route corridor (applied after the index search);
- sortBy: [{ "fields": ["updatedAt"], "direction": "asc" }]; without it results are ordered by relevance.

Only standard orders are searched (not quotes or other order types). External users (customers and suppliers) only see consignments of the businesses they are linked to; an external user without businesses sees nothing. Archived and deleted consignments are excluded. The request is recorded in the user's search history.

Permission: get:consignments

Errors: 400 when the body is not valid JSON, a date is not ISO-8601 or offset/amount is not an integer.

### Release a consignment from hold

 - [POST /consignment/{id}/release](https://docs.transportial.com/apis/consignment/release:consignment.md): Releases the consignment from hold: hold.onHold becomes false and the outbound events recorded while it was held are raised again with its current state. Trips that were held because of this consignment are released with it. Fires afterReleasingConsignment.

Permission: release:consignment

Errors: 400 with hold_not_on_hold; 404 when no consignment with this id exists on your platform.

### List the parcel carrier integrations of your platform

 - [GET /parcel/carriers](https://docs.transportial.com/apis/consignment/get:parcel:carriers.md): Returns the enabled parcel/express/freight carrier integrations of your platform (DHL Express, DHL Parcel NL/DE, DHL Freight, UPS, FedEx) with their services and default service. An empty list means the platform books and tracks no parcel shipments.

Permission: get:consignment

### Issue SSCCs to a consignment's goods and return its labels

 - [POST /consignment/{id}/sscc](https://docs.transportial.com/apis/consignment/update:consignment:sscc.md): Gives every goods line of the consignment that has no barcode an SSCC-18 under the platform's GS1 company prefix (goodsLabelSettings), then returns one label per goods line. Goods that already have a barcode keep it; transport equipment gets none. Calling it again issues nothing new.

Permission: update:consignment

Errors: 400 when no GS1 company prefix of 7 to 10 digits is set or its serial range is used up; 404 when no live consignment with this id exists on your platform.

### Cancel a consignment

 - [GET /consignment:cancel/{id}](https://docs.transportial.com/apis/consignment/cancel:consignment.md): Cancels a consignment that is not yet planned (draft, concept, requested or confirmed): its tender is closed and removed, its actions are realised and its status becomes cancelled. This is a GET with a side effect.

Permission: cancel:consignment

Errors: 400 when the consignment is already planned, in transit, completed or cancelled; 404 when no consignment with this id exists on your platform.

### Fetch the latest carrier tracking of a consignment's shipments now

 - [POST /consignment/{id}/parcel/refresh](https://docs.transportial.com/apis/consignment/update:consignment:parcel:refresh.md): Asks every carrier integration with open (not yet delivered, returned or cancelled) shipments on the consignment for their tracking now, instead of waiting for the next scheduled poll, and applies the result like the poll does.

Permission: update:consignment

Errors: 400 parcel_carrier_configuration when no integration could be asked because its settings are wrong; 502 parcel_carrier_error when no carrier answered; 404 when no live consignment with this id exists on your platform.

## Goods

Goods are the physical things transported on the platform, in two kinds selected by `type`: cargo items (`items`) and transport equipment (`transportEquipment`). Goods belong to consignments and transport orders, and trip load/unload actions are linked to them on save. Their dimensions, weights, quantity, load meters and cbm feed capacity, load-meter and stacking calculations. Missing load meters, cbm or dimensions are derived on save according to the platform planning settings.

### List goods

 - [GET /goodss/{offset}/{amount}](https://docs.transportial.com/apis/goods/get:goodss.md): Returns a page of goods (items and transport equipment) of your platform.

Paging uses offset and amount. Note: totalResults contains the number of goods on the returned page, not the total number of goods.

Permission: get:goodss

Errors: 400 when offset or amount is not a number.

### Search goods

 - [POST /goodss/{offset}/{amount}](https://docs.transportial.com/apis/goods/search:goodslist.md): Intended to search goods of your platform. The current implementation ignores the request body and returns the same page as GET /goodss/{offset}/{amount}.

Paging uses offset and amount. Note: totalResults contains the number of goods on the returned page, not the total number of goods.

Permission: search:goodsList

Errors: 400 when offset or amount is not a number.

### Get goods

 - [GET /goods/{id}](https://docs.transportial.com/apis/goods/get:goods.md): Returns one goods record of your platform by id. The response follows the goods type (items or transport equipment).

Permission: get:goods

Errors: 404 when no goods with this id exist on your platform.

### Delete goods

 - [DELETE /goods/{id}](https://docs.transportial.com/apis/goods/delete:goods.md): Permanently deletes a goods record of your platform (hard delete, including its cascaded documents, actors, actions, constraints and identifiers) and returns the deleted goods.

Permission: delete:goods

Errors: 404 when no goods with this id exist on your platform.

### Update goods

 - [PUT /goods](https://docs.transportial.com/apis/goods/update:goods.md): Updates a goods record identified by the id in the body. Only the fields present in the body are changed; missing metrics (load meters, cbm, dimensions) are derived again on save and the stacking metrics are marked for recalculation.

When the id is unknown on your platform a new goods record is created with the sent fields.

Permission: update:goods

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to goods of another platform.

### Create goods

 - [POST /goods](https://docs.transportial.com/apis/goods/create:goods.md): Creates a goods record (cargo items or transport equipment, selected by type) on your platform.

On save the server derives missing metrics: load meters from length and width (or length/width from load meters) and cbm from length, width and height (or missing dimensions from cbm), using the platform planning setting goodsWeightsAndLoadMetersAreTotalFields. Attributes are resolved to existing attributes, and the goods are linked to the load and unload sub-actions of any stop actions sent in actions.

Permission: create:goods

Errors: 400 when the body is not valid JSON, type is missing or unknown, or a field has the wrong type.

## TransportEquipment

Transport equipment is the goods subtype for trailers, chassis, containers, swap bodies and pallets. It carries load capacity (load dimensions, max weight, loading meters), identification (license plate, VIN, ISO 6346 container number) and telematics (sensors, board computers, latest location), and can contain other goods. Equipment created through the API belongs to your own fleet (`internal` = true) and can be attached to vehicles, planned on trips, managed by capacity contracts and soft-deleted. Equipment created by order imports stays out of fleet lists.

### Get transport equipment

 - [GET /transportEquipment/{id}](https://docs.transportial.com/apis/transportequipment/get:transportequipment.md): Returns one transport equipment record of your platform by id, including its board computers, latest location, capacity contract and pricing modules.

Permission: get:transportEquipment

Errors: 404 when no transport equipment with this id exists on your platform.

### Delete transport equipment

 - [DELETE /transportEquipment/{id}](https://docs.transportial.com/apis/transportequipment/delete:transportequipment.md): Soft-deletes transport equipment of your platform (sets deleted to true) and returns it. Equipment managed by a capacity contract cannot be deleted; remove it through the contract instead.

Permission: delete:transportEquipment

Errors: 400 when the equipment is managed by a capacity contract; 404 when no transport equipment with this id exists on your platform.

### Get latest sensor values of the equipment fleet

 - [GET /transportEquipments/sensors/latest/{offset}/{amount}](https://docs.transportial.com/apis/transportequipment/get:transportequipments:sensorslatest.md): Latest sensor snapshot of one page of your own transport equipment fleet, for fleet (FMS) dashboards: one row per equipment with the last value, unit and measurement time of each of its sensors. Only equipment with internal = true that is not archived, deleted or managed by a capacity contract is included. Equipment is ordered by last update.

Paging uses offset and amount (clamped to 1..100); totalResults holds the total number of equipment.

Permission: get:transportEquipments

### Search latest sensor values of the equipment fleet

 - [POST /transportEquipments/sensors/latest/{offset}/{amount}](https://docs.transportial.com/apis/transportequipment/search:transportequipments:sensorslatest.md): Latest sensor snapshot of one page of your own transport equipment fleet, for fleet (FMS) dashboards: one row per equipment with the last value, unit and measurement time of each of its sensors. Only equipment with internal = true that is not archived, deleted or managed by a capacity contract is included. An optional query in the body narrows the equipment by full-text search; a blank or missing body behaves like the GET variant.

Paging uses offset and amount (clamped to 1..100); totalResults holds the total number of matching equipment.

Permission: get:transportEquipments

### List extended transport equipment

 - [GET /transportEquipments:extended/{offset}/{amount}](https://docs.transportial.com/apis/transportequipment/get:transportequipments:extended.md): Same as GET /transportEquipments/{offset}/{amount}, but every equipment is extended with the vehicles it is attached to, the chauffeurs of those vehicles and its sensors.

Paging uses offset and amount; totalResults holds the total number of matching equipment.

Permission: get:transportEquipments:extended

Errors: 400 when offset or amount is not a number.

### Search extended transport equipment

 - [POST /transportEquipments:extended/{offset}/{amount}](https://docs.transportial.com/apis/transportequipment/search:transportequipments:extended.md): Full-text and filter search over your own fleet's transport equipment (internal = true, not archived or deleted) in the search index. Supports a phrase query, an availability window, contract-managed filtering, vehicle attachment and trip planning windows, and sorting; see the request body.

Paging uses offset and amount; totalResults holds the total number of matching equipment. Invalid dates or unsortable sort fields do not fail the request but return an empty page. Every result is extended with the vehicles it is attached to, the chauffeurs of those vehicles and its sensors.

Permission: search:transportEquipments:extended

Errors: 400 when the body is not valid JSON or offset/amount is not a number.

### Update transport equipment

 - [PUT /transportEquipment](https://docs.transportial.com/apis/transportequipment/update:transportequipment.md): Updates transport equipment identified by the id in the body. Only the fields present in the body are changed; a picture reference is resolved to an uploaded file. When the name or license plate changes, sensors named after the old value are renamed. When the id is unknown on your platform a new record is created with the sent fields.

Permission: update:transportEquipment

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to equipment of another platform.

### Create transport equipment

 - [POST /transportEquipment](https://docs.transportial.com/apis/transportequipment/create:transportequipment.md): Creates transport equipment (trailer, container, chassis, pallet, ...) in your own fleet.

The server always sets internal to true, resolves the picture reference to an uploaded file, and on save resolves business and boardComputers to existing records, uses equipmentId as name when no name is given, and derives loadLoadMeters from loadLength x loadWidth / 2.4 (or the missing load dimension from it). Load meters and cbm of the equipment itself are derived like for any goods.

Permission: create:transportEquipment

Errors: 400 when the body is not valid JSON or a field has the wrong type.

### List trips of a transport equipment

 - [GET /transportEquipment/{id}/trips/{offset}/{amount}](https://docs.transportial.com/apis/transportequipment/get:transportequipment:trips.md): Returns a page of trips on your platform that use the transport equipment, either directly or through the vehicle it is attached to, ordered by creation date. External users (customer accounts) only see trips of their own businesses.

Paging uses offset and amount; totalResults holds the total number of trips.

Permission: get:transportEquipment:trips

Errors: 400 when offset or amount is not a number; 404 when no transport equipment with this id exists on your platform.

### List transport equipment

 - [GET /transportEquipments/{offset}/{amount}](https://docs.transportial.com/apis/transportequipment/get:transportequipments.md): Returns a page of your own fleet's transport equipment: equipment with internal = true that is not archived or deleted, most recently updated first. Contract-managed equipment is included.

Paging uses offset and amount; totalResults holds the total number of matching equipment.

Permission: get:transportEquipments

Errors: 400 when offset or amount is not a number.

### Search transport equipment

 - [POST /transportEquipments/{offset}/{amount}](https://docs.transportial.com/apis/transportequipment/search:transportequipments.md): Full-text and filter search over your own fleet's transport equipment (internal = true, not archived or deleted) in the search index. Supports a phrase query, an availability window, contract-managed filtering, vehicle attachment and trip planning windows, and sorting; see the request body.

Paging uses offset and amount; totalResults holds the total number of matching equipment. Invalid dates or unsortable sort fields do not fail the request but return an empty page.

Permission: search:transportEquipments

Errors: 400 when the body is not valid JSON or offset/amount is not a number.

### List trips of several transport equipment

 - [POST /transportEquipments/trips:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/transportequipment/get:transportequipments:trips.md): Returns, for each transport equipment id in the body, the non-archived, non-deleted trips on your platform that use it (directly or through the vehicle it is attached to) and overlap the range: trips starting before toDate that have no end date or end after fromDate. Trips are returned as lean rows ordered by start date. External users only see trips of their own businesses.

Permission: get:transportEquipments:trips

Errors: 400 when the body is not valid JSON or transportEquipments is missing.

### Apply a bulk action to transport equipment

 - [POST /transportEquipments/bulk](https://docs.transportial.com/apis/transportequipment/bulk:transportequipment.md): Applies one action to several transport equipment records of your platform:

- delete: soft-deletes each equipment; contract-managed equipment is skipped.
- operational: sets operational to value (true when value is omitted).

Ids that do not exist on your platform are skipped. The response lists the ids that were actually updated.

Permission: delete:transportEquipment for delete, update:transportEquipment for any other action.

Errors: 400 when the body is not valid JSON, ids is empty or action is unknown.

### Get trips and calendar events of a transport equipment

 - [GET /transportEquipment/{id}/trips-and-events:byDateTime/{startDateTime}/{endDateTime}](https://docs.transportial.com/apis/transportequipment/get:transportequipment:trips-and-events:bydatetime.md): Returns the trips and calendar event occurrences of one transport equipment within a date-time range, for planning views.

Trips: non-archived, non-deleted trips that use the equipment (directly or through the vehicle it is attached to) and start within the range, ordered by start date. Events: occurrences of (recurring) calendar events planned for the equipment within the range.

Permission: get:transportEquipments:trips-and-events

Errors: 404 when no transport equipment with this id exists on your platform.

### Get trips, events and to-dos of several transport equipment

 - [POST /transportEquipments/trips-and-events:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/transportequipment/get:transportequipment:trips-and-events:bydate.md): Batch endpoint for planners: in one round-trip returns, per transport equipment id in the body,

- the trips that use it (directly or through its vehicle) and overlap the range, as lean rows (external users only see trips of their own businesses);
- its calendar event occurrences (recurring events expanded) within the range;
- the non-deleted to-dos linked to it whose deadline or creation time falls within the range.

Permission: get:transportEquipment:trips

Errors: 400 when the body is not valid JSON or transportEquipments is missing.

### List trips of a transport equipment by date

 - [GET /transportEquipment/{id}/trips:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/transportequipment/get:transportequipment:trips:bydate.md): Returns all non-archived, non-deleted trips on your platform that use the transport equipment (directly or through the vehicle it is attached to) and start between fromDate and toDate, ordered by start date. Not paged; totalResults is the number of trips returned.

Permission: get:transportEquipment:trips

Errors: 404 when no transport equipment with this id exists on your platform.

## AssignmentRule

Assignment rules (feature `assignmentRules`) set fields or assign a team, vehicle, chauffeur, transport equipment or capacity contract when a transport order, consignment or trip is created, changes status, or when a user applies them to selected entities. A rule has conditions on the entity JSON (including derived `facts.*` such as stop countries, goods weight and ADR), an optional decision table and actions; resource assignments may take the recommendation engine's best candidate. Rules run synchronously in `sequenceNr` order after the order defaults; within a group only the first match applies, and an action only fills an empty value unless it overwrites. Rule failures never block a save: they raise an alert. Multi-step or asynchronous reactions belong in workflows.

### Get an assignment rule

 - [GET /assignment-rule/{id}](https://docs.transportial.com/apis/assignmentrule/get:assignmentrule.md): Returns one assignment rule of your platform, with the names of the entities its actions reference.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: get:assignmentRule

Errors: 400 with assignment_rules_disabled; 404 when the rule does not exist on your platform.

### Update an assignment rule

 - [PUT /assignment-rule/{id}](https://docs.transportial.com/apis/assignmentrule/update:assignmentrule.md): Changes the keys present in the body; keys left out keep their value. The same validation as on create applies to the result.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: update:assignmentRule

Errors: 400 with assignment_rules_disabled or when the result is not a valid rule; 404 when the rule does not exist on your platform.

### Delete an assignment rule

 - [DELETE /assignment-rule/{id}](https://docs.transportial.com/apis/assignmentrule/delete:assignmentrule.md): Soft-deletes the rule; it no longer runs.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: delete:assignmentRule

Errors: 400 with assignment_rules_disabled; 404 when the rule does not exist on your platform.

### Get assignment rule options

 - [GET /assignment-rules/options](https://docs.transportial.com/apis/assignmentrule/get:assignmentruleoptions.md): The targets, triggers, action types, settable fields, statuses and the derived facts.* condition keys assignment rules can use.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: get:assignmentRules

Errors: 400 with assignment_rules_disabled.

### Draft an assignment rule with AI

 - [POST /assignment-rules/generate](https://docs.transportial.com/apis/assignmentrule/create:assignmentruledraft.md): Turns a plain-language description into a draft rule with the platform's AI model. Nothing is saved: review the draft and create it with the create endpoint. Names of teams, attributes and resources are resolved to ids of your platform; what cannot be expressed is listed in warnings.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: create:assignmentRule

Errors: 400 with assignment_rules_disabled, or when content is blank.

### Create an assignment rule

 - [POST /assignment-rules](https://docs.transportial.com/apis/assignmentrule/create:assignmentrule.md): Creates an assignment rule. Without sequenceNr it is added after the existing rules. Referenced teams, attributes, vehicles, chauffeurs, transport equipment and capacity contracts must exist on your platform; setField fields and values must be in the whitelist of the options endpoint.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: create:assignmentRule

Errors: 400 with assignment_rules_disabled, or when the name is blank, a trigger, status, action type, field or value is not valid for the target, or a reference is unknown.

### Search assignment rules

 - [POST /assignment-rules/{offset}/{amount}](https://docs.transportial.com/apis/assignmentrule/search:assignmentrules.md): Lists the assignment rules of your platform in evaluation order, filtered by text, target and active flag. legacyTeams lists teams whose own order assignment rules still run after the assignment rules.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: get:assignmentRules

Errors: 400 with assignment_rules_disabled, or when the body is not valid JSON or the target is unknown.

### Convert a team's order assignment rules

 - [POST /assignment-rules/teams/{teamId}/convert](https://docs.transportial.com/apis/assignmentrule/create:assignmentrulefromteam.md): Turns the enabled order assignment rules of a team into assignment rules (group teamAssignmentRules, after the existing rules, triggers onCreate and onStatusChange) and clears them on the team. Until converted, a team's rules keep running after the assignment rules.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: create:assignmentRule

Errors: 400 with assignment_rules_disabled; 404 when the team does not exist on your platform.

### Reorder assignment rules

 - [PUT /assignment-rules/order](https://docs.transportial.com/apis/assignmentrule/update:assignmentruleorder.md): Sets the evaluation order: the listed rules first in the given order, rules left out after them in their current order. sequenceNr is renumbered from 0. Returns every rule in the new order.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: update:assignmentRule

Errors: 400 with assignment_rules_disabled.

### Apply assignment rules now

 - [POST /assignment-rules/apply](https://docs.transportial.com/apis/assignmentrule/apply:assignmentrules.md): Runs the active rules with the manual trigger on each of the given transport orders, consignments or trips (at most 200) and saves the entities that changed. Applied rules are logged on the activity timeline; unknown ids are reported per entity.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: apply:assignmentRules

Errors: 400 with assignment_rules_disabled, or when ids is empty or longer than 200.

### Preview assignment rules on an entity

 - [POST /assignment-rules/preview](https://docs.transportial.com/apis/assignmentrule/get:assignmentrulepreview.md): Tests the rules against an existing transport order, consignment or trip without saving anything: every active rule of the target (whatever its trigger), or only the unsaved rule when given. Returns per rule whether it matched and per action what would change.

Requires the assignmentRules feature on your platform; without it every assignment rule endpoint answers 400 with assignment_rules_disabled.

Permission: get:assignmentRules

Errors: 400 with assignment_rules_disabled or when the draft rule is invalid; 404 when the entity does not exist on your platform.

## Incident

An incident is something that went wrong at a stop or with the cargo (refused by the consignee, address not found, closed, damaged goods, shortage, ...), feature `incidents`. Incidents are raised from failed actions in the driver app, the carrier portal or a board computer, or by a planner; a stop-level incident covers every consignment handled at that stop. Each incident appears on the exceptions list with one alternative per follow-up (retry, return to collection, retry at a hub, park at a depot, cancel, hold, no replan), which the planner picks; the follow-up creates the new legs or changes the status through the regular planning, hold and cancel mechanisms. Incident types, their default follow-up, customer visibility and the photo/remark rules are configured in `planningSettings.incidentSettings`; customer-visible incidents appear in the portal's order timeline, and the `afterCreatingIncident` / `afterResolvingIncident` events drive message automations.

### Report an incident

 - [POST /incident](https://docs.transportial.com/apis/incident/create:incident.md): Raises an incident as a planner: about an action (a stop-level type fails the open work of every consignment at that stop and the same address on the trip) and/or about consignments. The incident lands on the exceptions list as an INCIDENT exception with one alternative per follow-up (feature exceptions) and fires afterCreatingIncident. With followUp it is carried out right away.

Requires the incidents feature on your platform.

Permission: create:incident

Errors: 400 with incident_feature_disabled, incident_type_unknown, incident_subject_required, a follow-up error, or a missing required remark; 404 when the action, trip or a consignment does not exist on your platform.

### Carry out an incident follow-up

 - [POST /incident/{id}/followUp](https://docs.transportial.com/apis/incident/followup:incident.md): Carries out the follow-up for every consignment of the incident and resolves it (fires afterResolvingIncident); the matching exception alternative is accepted and the others close. New work is created as requested stops in the planning pool (or appended to tripId):
- retry: a new attempt at the same address, its time window moved to retryAt or by retryDelayDays;
- returnToCollection: an unload at the address where the goods were collected;
- retryAtHub / parkAtDepot: an unload at the location, a load there and the onward delivery;
- cancelConsignment: the consignment is cancelled (dead freight per deadFreightOnCancel);
- hold: the consignment is put on hold;
- noReplan: the failure is accepted as final and no longer keeps the consignment in replanning.
All-or-nothing: when one consignment cannot take the follow-up nothing changes.

Requires the incidents feature on your platform.

Permission: update:incident

Errors: 400 with incident_feature_disabled, incident_already_resolved, incident_follow_up_location_required, incident_follow_up_not_applicable or incident_follow_up_trip_closed; 404 when the incident, location or trip does not exist on your platform.

### Search incidents

 - [POST /incidents/{offset}/{amount}](https://docs.transportial.com/apis/incident/search:incidents.md): Lists the incidents of your platform, newest first, filtered by status, type, trip, consignment, transport order or text. totalResults counts the matches across all pages.

Requires the incidents feature on your platform.

Permission: get:incidents

Errors: 400 with incident_feature_disabled.

### Get an incident

 - [GET /incident/{id}](https://docs.transportial.com/apis/incident/get:incident.md): Returns one incident with its consignments, photos and follow-up.

Requires the incidents feature on your platform.

Permission: get:incident

Errors: 400 with incident_feature_disabled; 404 when no incident with this id exists on your platform.

### Update an incident

 - [PUT /incident/{id}](https://docs.transportial.com/apis/incident/update:incident.md): Re-classifies the incident, changes its remark or shows/hides it in the customer portal.

Requires the incidents feature on your platform.

Permission: update:incident

Errors: 400 with incident_feature_disabled or incident_type_unknown; 404 when no incident with this id exists on your platform.

## ReplanningSuggestion

A replanning suggestion is one ranked alternative for a planning exception on a trip or consignment. Some exceptions are raised inline (a failed action, a trip cancelled mid-execution, timing drift); others are found by the periodic exception sweep (overdue starts or actions, time-window breaches, missing vehicle or chauffeur, lost vehicle signal, consignments still unplanned near their window). All alternatives of one exception share an exceptionKey and severity. Planners evaluate (heuristic delay/cost/feasibility estimate), accept or dismiss a suggestion; open suggestions are marked RESOLVED once the condition disappears. Suggestions are created by the server only.

### Dismiss a replanning suggestion

 - [POST /replanning-suggestions/{id}/dismiss](https://docs.transportial.com/apis/replanningsuggestion/update:replanning-suggestion:dismiss.md): Rejects this alternative for its planning exception: a PENDING suggestion becomes DISMISSED and drops off the planner's open list.

Calling this on a suggestion that is no longer PENDING changes nothing and returns it as it is. Other alternatives raised for the same exception keep their status. Nothing on the trip or consignment is changed.

The returned suggestion does not include the trip summary fields (tripName, vehicleName, ...); those are only filled by the search endpoint. No request body is read.

Permission: update:replanning-suggestion

Errors: 404 when no replanning suggestion with this id exists on your platform.

### Accept a replanning suggestion

 - [POST /replanning-suggestions/{id}/accept](https://docs.transportial.com/apis/replanningsuggestion/accept:replanning-suggestion.md): Records the planner's choice of this alternative for its planning exception: a PENDING suggestion becomes ACCEPTED.

The transition is a status change only: the proposed changes are not applied to the trip or consignment yet, so the planner still carries out the replanning. Calling this on a suggestion that is no longer PENDING (already accepted, dismissed or resolved by the system) changes nothing and returns the suggestion as it is. Other alternatives raised for the same exception keep their status.

The returned suggestion does not include the trip summary fields (tripName, vehicleName, ...); those are only filled by the search endpoint. No request body is read.

Permission: accept:replanning-suggestion

Errors: 404 when no replanning suggestion with this id exists on your platform.

### Evaluate a replanning suggestion

 - [POST /replanning-suggestions/{id}/evaluate](https://docs.transportial.com/apis/replanningsuggestion/update:replanning-suggestion:evaluate.md): Estimates the impact of this alternative and stores it on the suggestion: sets feasible, delayMinutes and costDelta, and marks feasibilityChecked as true.

The estimate is a heuristic based on the suggestion type and the trip it belongs to (number of actions, planned duration and route distance), not a solver run:
- MINIMAL_CHANGE: 5 minutes per action on the trip (at most 60), no cost delta;
- CROSS_TRIP: 30 minutes plus 1 minute per 50 km of trip distance, cost delta 0.05 per km;
- REPLAN_ALL: 25% of the trip duration (at least 15 minutes), cost delta 0.10 per km;
- ASSIGN_RESOURCE, NOTIFY_CUSTOMER, CONTACT_DRIVER, MANUAL_REVIEW: no delay and no cost delta.

Every type is currently reported as feasible. The evaluation runs whatever the suggestion's status and can be repeated; the status itself is not changed. The returned suggestion does not include the trip summary fields; those are only filled by the search endpoint. No request body is read.

Permission: update:replanning-suggestion

Errors: 404 when no replanning suggestion with this id exists on your platform.

### Search replanning suggestions

 - [POST /replanning-suggestions/{offset}/{amount}](https://docs.transportial.com/apis/replanningsuggestion/search:replanning-suggestions.md): Lists the replanning suggestions (planning exceptions with their alternatives) of your platform, one page at a time. Archived and deleted suggestions are never returned.

The JSON body is optional; every filter in it is optional and an empty body lists all suggestions. Filters are combined with AND, values inside one list with OR:
- statuses: array of PENDING, ACCEPTED, DISMISSED, RESOLVED (the single status string is still accepted too);
- exceptionTypes, severities, suggestionTypes: arrays of the respective enum values (case-insensitive; unknown values are ignored);
- evaluation: one of evaluated, not_evaluated, feasible, infeasible;
- trips / tripIds: trip ids; vehicles: vehicle ids of the trip; vehicleGroups: vehicle group or fleet ids; chauffeurs: chauffeur ids on the trip;
- query: case-insensitive substring of the reasoning, trip name or trip number;
- date-range: { "start": ISO-8601, "end": ISO-8601 } on the creation time (unparseable values are ignored);
- sortBy: [{ "fields": ["createdAt"], "direction": "asc" }]; only the first entry and field are used, the field must be one of createdAt, severity (most severe first when descending), delayMinutes, costDelta, rank; default createdAt descending. Ties are ordered by trip and rank.

Each suggestion in the page is enriched with a read-only summary of its trip (name, number, status, start date, vehicle and chauffeur names). If the query fails the endpoint answers with an empty page rather than an error.

Permission: get:replanning-suggestion

Errors: 400 when the body is not valid JSON or offset/amount is not an integer.

## CustomerPortal

Order tools of the customer portal for `customer` accounts: order cut-off times (when rules are configured) checked when a portal order is placed, change and cancellation requests on their own orders until collection (when enabled) that planners approve or reject, or that apply at once while nothing is planned, and document upload to their own orders (when enabled). Settings live in `customerPortalSettings.portalOrders` with per-customer overrides in `business.portalOrderSettings`; notifications go only through message automations on the change request trigger events.

### Request a change or cancellation of your own order

 - [POST /transportOrder/{id}/changeRequest](https://docs.transportial.com/apis/customerportal/create:transportorder:changerequest.md): A customer portal account asks to change (kind change, with changes) or cancel (kind cancel) one of its own orders, when change requests are enabled for it (customerPortalSettings.portalOrders.changeRequests.enabled, or the customer's business.portalOrderSettings.changeRequestsEnabled). Accepted until the platform's window closes (customerPortalSettings.portalOrders.changeRequests.window: before loading starts, before a trip is confirmed or a number of hours before loading) and while no other request of the order is pending. Only the categories in allowedChanges may be changed; an address change must use a location the customer may use; a date change that moves a loading stop past its cut-off is refused when the platform blocks late orders.

With mode autoWhenUnplanned and nothing of the order planned yet (and the order not on hold), the request is applied at once through the order's normal update and answered approved with autoApplied = true; otherwise it waits for a planner. Queues afterCreatingOrderChangeRequest (and afterApprovingOrderChangeRequest when applied at once).

Permission: create:orderChangeRequest (or create:transportOrder)

Errors: 400 order_change_request_disabled, order_change_request_window_closed (params.reason), order_change_request_not_allowed (params.category), order_change_request_invalid, transportOrder_cut_off_passed; 401 when the caller is not a customer account; 404 when the order is not the caller's.

### Check a loading moment against the order cut-off

 - [POST /portal/cutOff/check](https://docs.transportial.com/apis/customerportal/check:portal:cutoff.md): Answers whether an order loading at loadingDateTime is past its cut-off, when the cut-off is or was, and the earliest loading date whose cut-off has not passed. Rules come from the customer's business.portalOrderSettings.cutOffRules first, then customerPortalSettings.portalOrders.cutOff.rules; a rule naming one of consignmentTypes beats a generic rule, and of several matching rules the strictest applies. Times are read in the loading location's time zone (loadingLocationId) or the platform's, per rule.

Portal customers always check against their own business; internal users may pass customerBusinessId. Answers applies = false when no rule matches or the customer is exempt.

Permission: create:transportOrder

### Upload documents to your own order

 - [POST /transportOrder/{id}/portalDocuments](https://docs.transportial.com/apis/customerportal/create:transportorder:portaldocuments.md): A customer portal account attaches documents to one of its own orders, or to one of its consignments, when customerPortalSettings.portalOrders.documentUpload.enabled is on: one per file uploaded with POST /file/upload. Files of another platform, files already attached to a document, types not in customerPortalSettings.portalOrders.documentUpload.documentTypes, MIME types not allowed and files over maxFileSizeMb are refused. Documents are marked as provided by the customer (externalAttributes.customerProvided, document analysis source portal) and typed with the chosen type, so required documents count them; with the documentIntelligence feature they are queued in the document inbox, linked to the order.

Permission: upload:portalDocument (or create:transportOrder)

Errors: 400 portal_document_upload_disabled, portal_document_invalid; 401 when the caller is not a customer account; 404 when the order is not the caller's.

### List the change requests of a transport order

 - [GET /transportOrder/{id}/changeRequests](https://docs.transportial.com/apis/customerportal/get:transportorder:changerequests.md): Returns the customer portal change and cancellation requests of the order, newest first, and whether the customer may make a new one now (window). Requires change requests to be enabled for the order's customer. Portal customers only reach their own orders; any other id answers 404.

Permission: get:orderChangeRequests (or get:transportOrder)

Errors: 400 order_change_request_disabled when change requests are disabled; 404 when the order is not found or not the caller's.

### Approve a customer's change request

 - [POST /orderChangeRequest/{id}/approve](https://docs.transportial.com/apis/customerportal/approve:orderchangerequest.md): Applies a pending customer portal change request to the order as it is now, through the order's normal update (credit control, quote sync, activity, integrations), or cancels the order for a cancellation request. A held order is refused (transportOrder_on_hold). Queues afterApprovingOrderChangeRequest; the customer sees the outcome and note.

Internal users only.

Permission: approve:orderChangeRequest (or update:transportOrder)

Errors: 400 order_change_request_not_pending, order_change_request_invalid (the order no longer has a changed stop or goods item), transportOrder_on_hold; 401 for portal accounts; 404 when the request is not found.

### Withdraw your own pending change request

 - [POST /orderChangeRequest/{id}/withdraw](https://docs.transportial.com/apis/customerportal/withdraw:orderchangerequest.md): The customer takes back a pending change or cancellation request of one of its own orders.

Permission: create:orderChangeRequest (or create:transportOrder)

Errors: 400 order_change_request_not_pending when it was already decided; 401 when the caller is not a customer account; 404 when the request or its order is not the caller's.

### Reject a customer's change request

 - [POST /orderChangeRequest/{id}/reject](https://docs.transportial.com/apis/customerportal/reject:orderchangerequest.md): Rejects a pending customer portal change or cancellation request; the order is not touched. Queues afterRejectingOrderChangeRequest; the customer sees the outcome and note.

Internal users only.

Permission: approve:orderChangeRequest (or update:transportOrder)

Errors: 400 order_change_request_not_pending; 401 for portal accounts; 404 when the request is not found.

### Get the portal order settings for the signed-in customer

 - [GET /portal/orderSettings](https://docs.transportial.com/apis/customerportal/get:portal:ordersettings.md): Returns what the customer portal offers the signed-in customer account for its orders: whether cut-off times apply (cut-off rules exist and the customer is not exempt), how change and cancellation requests are handled (with the customer's own overrides from business.portalOrderSettings) and which document types it may upload. A part that is not enabled answers enabled = false.

Only customer accounts may call this.

Errors: 401 when the caller is not a customer account.

## Trip

A trip is the executable plan for one vehicle or a forwarded carrier: an ordered sequence of stop actions (with sub-actions such as load and unload) connected by the move actions of its calculated route, together with the consignments, chauffeurs, transport equipment, documents and financials it carries. Its status runs requested/accepted -> confirmed -> in_transit -> (pending_completion ->) completed, or cancelled; confirming plans its actions, starting projects them and finishing realises them and queues integration triggers. Trips under a capacity contract become forwarded and get a carrier booking. Every save re-binds consignments and goods, recalculates estimated times and may regenerate the route. Drivers without an account can execute a trip through its share code.

### Start a trip

 - [GET /trip/{id}/start](https://docs.transportial.com/apis/trip/start:trip.md): Starts a trip: sets its status to IN_TRANSIT, sets the start date (the earliest start of actions that
already executed, otherwise now) and projects the actions that have not started yet. When the vehicle has a last
known position more than 500 m from the first stop, a move from that position (or from the last stop of the
previous trip for a linked trip) is prepended to the route and started; when it is closer, the first stop is started.
Queues the after-starting-trip integration trigger and logs a trip activity. Starting a trip that is already in
transit changes nothing.

Pre-trip check (planningSettings.tripStartCheck.enabled): a trip that has not started yet
is refused with 400 trip_start_check_required while it has no check and requiredBeforeStart is set, and with
trip_start_check_failed while its check failed and blockStartOnFailure is set. A check of the same vehicle submitted
on another trip within validityHours is copied onto the trip (with reusedFromTripId) before deciding. Starts by
integrations and board computers are never gated.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. This is what the public driver trip-execution page uses.

Permission: start:trip, or a valid shareCode query parameter

Errors: 400 with trip_start_check_required or trip_start_check_failed (see above); 401 when the share code does not match an open, unexpired share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used).

### Finish a trip and start the next one

 - [GET /trip/{id}/finish/{nextId}/start](https://docs.transportial.com/apis/trip/finish:start:trip.md): Finishes the trip id (as GET /trip/{id}/finish) and starts the trip nextId (as GET /trip/{id}/start),
using the last stop of the finished trip as the starting position of the next one. Returns the finished trip.
The next trip is subject to the same pre-trip check gate as GET /trip/{id}/start; when it is refused, neither trip changes.

Permission: finish:start:trip

Errors: 400 with trip_start_check_required or trip_start_check_failed when the next trip's pre-trip check is missing or failed; 404 when either trip does not exist on your platform.

### Get combined financials of linked trips

 - [GET /trip/{id}/financials/combined](https://docs.transportial.com/apis/trip/get:trip:financials:combined.md): Returns the expected and actual revenue and cost of the trip itself, and combined over the chain of linked
trips it belongs to: the vehicle's trips (excluding cancelled and deleted trips, on the same platform) ordered
by start date, joined while each next trip is marked as linked. The response is the financials object itself,
without a success envelope.

Permission: get:trip

Errors: 404 when no trip with this id exists on your platform.

### List trips as lean rows

 - [GET /trips/list/{offset}/{amount}](https://docs.transportial.com/apis/trip/get:trips:list.md): Returns one page of trips of your platform as lean list rows (id, uuid, name, platform and timestamps
only), plus the total number of trips. No associations are serialized, so this is the fast path for
scrolling lists; fetch GET /trip/{id} to open or edit a trip.

Permission: get:trips

Errors: none specific to this endpoint.

### Search trips as lean rows

 - [POST /trips/list/{offset}/{amount}](https://docs.transportial.com/apis/trip/search:trips:list.md): Search variant of GET /trips/list/{offset}/{amount}: returns one page of matching trips as lean list rows,
plus the total number of matches.

The body is a trip search filter (SearchTripsRequest). All filters are optional: query is a
free-text search over the indexed trip fields, the other fields narrow on chauffeur, type, status, plan status,
route status, transport mode, teams, businesses, customers and date range; sortBy controls ordering.
Results are restricted to your platform. The search body is also stored in your search history.

Permission: get:trips

Errors: 400 when the body is malformed or a value cannot be parsed.

### List trips as plan-board rows

 - [GET /trips/board/{offset}/{amount}](https://docs.transportial.com/apis/trip/get:trips:board.md): Returns one page of trips of your platform as plan-board rows: the columns the plan board renders, built
by a dedicated projection query instead of serializing whole trips, plus the total number of trips on the
platform. Paging follows the same order as GET /trips/{offset}/{amount}. Fetch GET /trip/{id} for the full
trip before editing it.

Permission: get:trips

Errors: none specific to this endpoint.

### Search trips as plan-board rows

 - [POST /trips/board/{offset}/{amount}](https://docs.transportial.com/apis/trip/search:trips:board.md): Search variant of GET /trips/board/{offset}/{amount}: the search index ranks the page and the board
projection fills in the row columns for those trips, in that order.

The body is a trip search filter (SearchTripsRequest). All filters are optional: query is a
free-text search over the indexed trip fields, the other fields narrow on chauffeur, type, status, plan status,
route status, transport mode, teams, businesses, customers and date range; sortBy controls ordering.
Results are restricted to your platform. The search body is also stored in your search history.

Permission: get:trips

Errors: 400 when the body is malformed or a value cannot be parsed.

### Put a trip on hold

 - [POST /trip/{id}/hold](https://docs.transportial.com/apis/trip/hold:trip.md): Puts the trip on hold (do-not-dispatch): hold.onHold becomes true with the reason, the user and the time. While held, nothing about it is sent to drivers, board computers, integrations, message automations, workflows, app webhooks or carriers; those outbound events are recorded and raised again on release. A held trip cannot be confirmed, started or offered to drivers, and a held order cannot be accepted. Cancellations still go out. Fires afterHoldingTrip.

Permission: hold:trip

Errors: 400 with hold_already_on_hold, hold_reason_required (a reason is required by the settings), hold_not_allowed_in_status (the trip has started, finished or was cancelled); 404 when no trip with this id exists on your platform.

### Record unplanned work on a trip

 - [POST /trip/{id}/action:unplanned](https://docs.transportial.com/apis/trip/create:trip:action:unplanned.md): Records work the driver did that was not planned, such as a refuel, weighing, customs check or break. When
the driver is at a stop of the trip, the activity is added to that stop and the route is untouched. Otherwise a
new stop is created at the reported position (lat/lon, falling back to the vehicle's last known position),
reusing a nearby location or creating one, and the move covering that leg is split at it. Unknown type values
become a generic action; startTime defaults to now. Saves the trip.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. This is what the public driver trip-execution page uses.

Permission: update:route, or a valid shareCode query parameter

Errors: 400 when the body is malformed or a value cannot be parsed; 401 when the share code does not match an open, unexpired share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used).

### Get the budget of a trip

 - [GET /trip/{id}/budget](https://docs.transportial.com/apis/trip/get:trip:budget.md): Returns the budget locked when the trip first went in transit, the trip's current
expected cost and revenue, and its actual cost and revenue from booked invoices (its share of actual ledger entries),
with the variances against the budget. The budget is null until the trip has been in transit.

Permission: get:ledgerEntriesByEntity

Errors: 404 when no trip with this id exists on your platform.

### Release a trip from hold

 - [POST /trip/{id}/release](https://docs.transportial.com/apis/trip/release:trip.md): Releases the trip from hold: hold.onHold becomes false and the outbound events recorded while it was held are raised again with its current state. Fires afterReleasingTrip.

Permission: release:trip

Errors: 400 with hold_not_on_hold; 404 when no trip with this id exists on your platform.

### Update several trips

 - [PUT /trips](https://docs.transportial.com/apis/trip/update:trips:bulk.md): Updates every trip in trips with the same merge rules as PUT /trip; each trip is saved and committed on
its own, so trips updated before a failure stay updated.

Permission: update:trip

Errors: 400 when a trip has no id, the body is malformed, or a trip fails validation; 404 when one of the
trips does not exist on your platform.

### Create several trips

 - [POST /trips](https://docs.transportial.com/apis/trip/create:trips:bulk.md): Creates every trip in the body (a JSON array of trips) and returns them. Each trip is saved and committed
on its own, with the same server-side processing as POST /trip, so trips saved before a failure stay created.

Permission: create:trip

Errors: 400 when the body is malformed or a value cannot be parsed, the body is not a JSON array, or a trip fails validation.

### Finish a trip

 - [GET /trip/{id}/finish](https://docs.transportial.com/apis/trip/finish:trip.md): Finishes a trip: settles the start and end date from the executed stops, realizes all actions and detaches
the trip from the vehicle when it is the vehicle's current trip. When the platform requires planner completion
the status becomes PENDING_COMPLETION (complete it with GET /trip/{id}/complete); otherwise it becomes
COMPLETED, end-of-trip invoices are created and the after-finishing-trip integration trigger is queued.
A trip that is already PENDING_COMPLETION, COMPLETED or CANCELLED is returned unchanged, so a retried call is safe.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. This is what the public driver trip-execution page uses.

Permission: finish:trip, or a valid shareCode query parameter

Errors: 401 when the share code does not match an open, unexpired share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used).

### Create a trip and calculate its route

 - [POST /trip:route](https://docs.transportial.com/apis/trip/create:trip:route.md): Creates a trip from the body and returns it, including its calculated route. Behaves exactly like
POST /trip: the route is calculated during save unless the body supplies a calculated route.

Permission: create:trip

Errors: 400 when the body is malformed or a value cannot be parsed or the trip fails validation.

### Confirm a trip

 - [GET /trip/{id}/confirm](https://docs.transportial.com/apis/trip/confirm:trip.md): Confirms a trip: sets its status to CONFIRMED and its actions to the PLANNED life cycle, sends the
trip's booking to the carrier when it has one, queues the after-confirming-trip integration trigger and logs a
trip activity.

Permission: confirm:trip

Errors: 404 when no trip with this id exists on your platform.

### Get a trip

 - [GET /trip/{id}](https://docs.transportial.com/apis/trip/get:trip.md): Returns one trip with its full detail: actions, route, consignments, vehicle, chauffeurs, transport
equipment, documents, financials and settings, as visible to the caller.

Permission: get:trip

Errors: 404 when no trip with this id exists on your platform.

### Delete a trip

 - [DELETE /trip/{id}](https://docs.transportial.com/apis/trip/delete:trip.md): Deletes the trip and all capacity change events of the trip (whatever their life cycle), and returns the
trip as it was before deletion.

Permission: delete:trip

Errors: 404 when no trip with this id exists on your platform.

### Report the position of a trip's vehicle

 - [POST /trip/{id}/locationUpdate](https://docs.transportial.com/apis/trip/create:trip:locationupdate.md): Records a GPS position for the vehicle of the trip, exactly as if a board computer reported it: creates a
location update event, refreshes the vehicle's latest location, recalculates the ETA and progresses the trip's
stop actions. Only geoReference is read from the body: either a lat/lon point object or the id of an existing
lat/lon geo reference.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. This is what the public driver trip-execution page uses.

Permission: update:trip, or a valid shareCode query parameter

Errors: 400 when the body is malformed or has no geoReference; 401 when the share code does not match an open, unexpired share of the trip; 404 when the trip does
not exist, the geo reference is not a lat/lon point, or the trip has no vehicle.

### Change the life cycle of a trip action

 - [PUT /trip/{id}/action/{actionId}](https://docs.transportial.com/apis/trip/update:trip:action:lifecycle.md): Moves one action of the trip to a life cycle. ACTUAL starts the action (and its sub-actions); REALIZED
stores result and ends it, closing the parent stop once all its sub-actions are realized; a failed result or a
requested reschedule hands the action to replanning. Other values set the life cycle directly. Queues
the after-action-life-cycle-update integration trigger and saves the trip. When no action of the trip has
actionId, only the trip is saved.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. This is what the public driver trip-execution page uses.

Permission: update:route, or a valid shareCode query parameter

Errors: 400 when the body is malformed or a value cannot be parsed; 401 when the share code does not match an open, unexpired share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used).

### Swap the order of two trips

 - [PUT /vehicle/{id}/trips:swap](https://docs.transportial.com/apis/trip/update:vehicle:trips.md): Swaps the order of two trips of a vehicle: trip2 takes over the start date of trip1, and trip1 is moved to start the duration of its own route later (or now when it had no start date). Both trips are saved, the in-transit one first, which recalculates their planning.

Only the trips' ids are read from the body; both must belong to your platform. The id path segment is not used.

Permission: update:vehicle

Errors: 400 when the body is not valid JSON; 404 when either trip does not exist on your platform.

### Update an action of a trip

 - [PUT /trip/{id}/action](https://docs.transportial.com/apis/trip/update:trip:action.md): Merges action from the body onto the matching action of the trip (looked up by action.id among all stop
actions and their sub-actions) and returns the trip. When no action of the trip has that id, nothing changes.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. This is what the public driver trip-execution page uses to add stop photos: on this path ONLY action.documents is applied, and only NEW documents around files just uploaded for the trip (POST /file/upload with the share code) are added. Every other field of the action is ignored; lifecycle and results go through PUT /trip/{id}/action/{actionId} and PUT /trip/{id}/actions/lifecycle.

Permission: update:trip:action, or a valid shareCode query parameter

Errors: 400 when the body is malformed or a value cannot be parsed, or (share code) a document names an existing document or a file that is not a fresh upload of the trip's platform (document_attach_invalid); 401 when the share code does not match an open, unexpired share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used), or (session) the action names a document of another platform.

### Cancel several trips

 - [POST /trips/cancel](https://docs.transportial.com/apis/trip/cancel:trips:bulk.md): Cancels every trip in tripIds exactly like GET /trip/{id}/cancel, committing after each trip. Ids that do
not resolve to a trip on your platform are skipped silently.

Permission: cancel:trip

Errors: 400 when the body is malformed or a value cannot be parsed.

### Update a trip

 - [PUT /trip](https://docs.transportial.com/apis/trip/update:trip.md): Updates the trip identified by id in the body by merging the body onto the stored trip, then saves it
(route recalculation, stop renumbering, consignment binding and integration triggers as on create).

Actions left out of actions are taken off the trip and returned to planning, except actions that were
already executed: those always stay on the trip. A consignment leaves the trip only when every one of its
actions was left out and none was executed. Omitted action links are kept, never cleared. When the trip
status changes, the activity of the assigned chauffeurs follows (driving, work or available).

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. This is what the public driver trip-execution page uses.

With a share code the money fields the link never received (prices, costs, quotes, invoices, route cost and expenses) are kept from storage: the link can neither clear nor set them, and the trip comes back without them.

Permission: update:trip, or a valid shareCode query parameter

Errors: 400 when the body has no id, is malformed, or the trip fails validation; 401 when the share code does not match an open, unexpired share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used).

### Create a trip

 - [POST /trip](https://docs.transportial.com/apis/trip/create:trip.md): Creates a trip from the body and returns it. On save the server resolves action locations to existing
locations, numbers the stops, groups actions per stop, calculates the route (distance, durations, CO2) unless a
calculated route is supplied, binds consignments and goods to the actions, derives the trip type (a capacity
contract makes it FORWARDED, managing the virtual vehicle, transport equipment or chauffeur), copies
chauffeurs and transport equipment from the vehicle, and queues the matching integration triggers.

Permission: create:trip

Errors: 400 when the body is malformed or a value cannot be parsed or the trip fails validation.

### Get the pre-trip check of a trip

 - [GET /trip/{id}/startCheck](https://docs.transportial.com/apis/trip/get:tripstartcheck.md): Returns whether the trip still needs its pre-trip vehicle check (required), the check submitted for it (startCheck) and the check configuration of the trip's platform (settings: questions, result mappings and rules) for the driver to fill in.

The check is in force when the trip's platform has planningSettings.tripStartCheck.enabled set to true; otherwise the answer is required: false with startCheck and settings null. required is false as well when a check of the same vehicle, submitted on another trip within validityHours, can be reused (it is copied onto the trip when the trip starts).

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session, as for starting the trip.

Permission: start:trip, or a valid shareCode query parameter

Errors: 401 when the share code does not match an open share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used).

### Submit the pre-trip check of a trip

 - [POST /trip/{id}/startCheck](https://docs.transportial.com/apis/trip/submit:tripstartcheck.md): Stores the driver's pre-trip vehicle check on the trip (trip.startCheck), replacing an earlier check while the trip has not started. The answers are validated against the platform's planningSettings.tripStartCheck questions like an action result questionnaire (only conditions on answers are evaluated); the status comes from the result mappings or a chosen option's setsResultStatus, else succeeded. Inline photos and signatures (dataUrl) are stored as documents of the trip and referenced by id.

A failed check records a damage on the trip's vehicle (name "Pre-trip check failed", the failing answers and reason as description, the photos as documents) when reportDefectsAsDamage is set and the trip has a vehicle, and fires afterTripStartCheckFailed.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session, as for starting the trip.

Permission: start:trip, or a valid shareCode query parameter

Errors: 400 with trip_start_check_disabled (check not enabled), trip_already_started (the trip is in transit or later), or trip_start_check_invalid (answers fail validation; errors lists why); 401 when the share code does not match; 404 when the trip or a referenced document does not exist.

### Change the times of a trip action

 - [PUT /trip/{id}/action/{actionId}/times](https://docs.transportial.com/apis/trip/update:trip:action:times.md): Sets the start and end time of one action of the trip. For a stop action the preceding move action ends at
the new start time and the following move action starts at the new end time. Saves the trip. When no action of
the trip has actionId, only the trip is saved.

Permission: update:route

Errors: 400 when the body is malformed or a value cannot be parsed; 404 when no trip with this id exists on your platform.

### Complete all actions of a trip

 - [PUT /trip/{id}/complete-actions](https://docs.transportial.com/apis/trip/complete:trip:actions.md): Completes every action of the trip in one call, for customers that use neither driver links nor
integrations. Stop actions (with their sub-actions) that are not yet realized are ended in sequence order,
then the move actions of the route; move actions already realized keep their original times. Each action gets
its per-action times and result from actions, else the defaults (times default to now, result to an empty
result). Queues the after-action-life-cycle-update integration trigger per action and saves the trip. The body
is optional; an empty or unparsable body uses the defaults.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. This is what the public driver trip-execution page uses.

Permission: update:route, or a valid shareCode query parameter

Errors: 401 when the share code does not match an open, unexpired share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used).

### Complete a trip pending completion

 - [GET /trip/{id}/complete](https://docs.transportial.com/apis/trip/complete:trip.md): Completes a trip that a driver finished while the platform requires planner completion: sets the status from
PENDING_COMPLETION to COMPLETED, creates the end-of-trip invoices, queues the after-finishing-trip
integration trigger and logs a trip activity. A trip that is already COMPLETED is returned unchanged.

Permission: complete:trip

Errors: 400 when the trip is neither PENDING_COMPLETION nor COMPLETED; 404 when no trip with this id exists on your platform.

### List trips

 - [GET /trips/{offset}/{amount}](https://docs.transportial.com/apis/trip/get:trips.md): Returns one page of trips of your platform with the full trip payload, plus the total number of trips.
For large lists prefer the lean projections GET /trips/list/{offset}/{amount} or
GET /trips/board/{offset}/{amount}; use POST /trips/{offset}/{amount} to filter.

Permission: get:trips

Errors: none specific to this endpoint.

### Search trips

 - [POST /trips/{offset}/{amount}](https://docs.transportial.com/apis/trip/search:trips.md): Searches the trips of your platform and returns one page of matching trips with the full trip payload,
plus the total number of matches.

The body is a trip search filter (SearchTripsRequest). All filters are optional: query is a
free-text search over the indexed trip fields, the other fields narrow on chauffeur, type, status, plan status,
route status, transport mode, teams, businesses, customers and date range; sortBy controls ordering.
Results are restricted to your platform. The search body is also stored in your search history.

Permission: get:trips

Errors: 400 when the body is malformed or a value cannot be parsed.

### Cancel a trip

 - [GET /trip/{id}/cancel](https://docs.transportial.com/apis/trip/cancel:trip.md): Cancels a trip: sets its status to CANCELLED and its end date to now, returns actions that were not
executed to planning (per the platform's trip cancellation settings) and hands consignments with unfinished
work back to the planner. Detaches the trip from the vehicle when it is the vehicle's current trip, deletes its
capacity change events that are not actual or realized, queues the after-canceling-trip integration trigger,
sends the cancellation to the carrier of a booking, and cancels the expected ledger entries of the trip.

Permission: cancel:trip

Errors: 404 when no trip with this id exists on your platform.

### Plan optimized trips

 - [POST /trip/optimizer](https://docs.transportial.com/apis/trip/create:trips:optimized.md): Runs the platform's trip optimizer (the planning settings' optimizer engine, PTV by default) over the given
locations, consignments, vehicles, transport equipment and chauffeurs and returns the proposed trips plus the
consignments that could not be planned. Ids that do not resolve on your platform are ignored. When the
optimizer fails, the response has success: false, no trips, and every consignment as left over.

Permission: create:trips

Errors: 400 when the body is malformed or a value cannot be parsed.

### Get a shared trip by share code

 - [GET /trip/{id}/{shareCode}](https://docs.transportial.com/apis/trip/get:trip:public.md): Returns one trip through its share link, without a user session. The trip is looked up regardless of
platform and returned only when its share settings are open and the share code in the path matches; the
request then acts on the trip's own platform. Used by the public trip page and the driver trip-execution page.

Financial privacy: the trip keeps its shape but carries no money fields (expectedCost, expectedRevenue, actualCost, pricingElements, quotes, invoices, route cost and expenses, ledgerEntries, lockedBudget); capacityContract is cut to the carrier's identity.

Permission: Public, no authentication (authorized by the share code).

Errors: 401 when the share is closed, the share link has expired (validUntil) or the share code does not match; 404 when no trip with this id exists.

### Change the life cycle of several trip actions

 - [PUT /trip/{id}/actions/lifecycle](https://docs.transportial.com/apis/trip/update:trip:actions:lifecycle.md): Moves several actions of one trip through the same life-cycle step in one transaction, as when a driver
starts or completes all sub-actions of a grouped stop. Each action gets the same treatment as
PUT /trip/{id}/action/{actionId}; a per-action result overrides the shared one. Ids that do not resolve on
the trip are skipped. Returns the trip once, after all actions are processed.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. This is what the public driver trip-execution page uses.

Permission: update:route, or a valid shareCode query parameter

Errors: 400 when the body is malformed or a value cannot be parsed; 401 when the share code does not match an open, unexpired share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used).

### Get the activity log of a trip

 - [GET /trip/{id}/activity](https://docs.transportial.com/apis/trip/get:trip:activity.md): Returns the activity log of the trip, newest first: creation, status changes, starts, completions, changed
fields, stops, drivers, equipment and consignments added or removed, and stop changes (times, location, status).
Each entry carries its field-level changes (old and new value per field), the part it happened on (subjectType, subjectLabel, e.g. a stop or a line) and its origin (source: user, API, integration or system). Paginated with the offset and amount query parameters; totalResults is the total number of entries.

Permission: get:trip

Errors: 404 when no trip with this id exists on your platform.

### Confirm several trips

 - [POST /trips/confirm](https://docs.transportial.com/apis/trip/confirm:trips:bulk.md): Confirms every trip in tripIds exactly like GET /trip/{id}/confirm, committing after each trip. Ids that
do not resolve to a trip on your platform are skipped silently.

Permission: confirm:trip

Errors: 400 when the body is malformed or a value cannot be parsed.

### Attach documents to a trip

 - [PUT /trip/{id}/documents](https://docs.transportial.com/apis/trip/update:trip:documents.md): Adds the documents in the body to the trip's documents; documents already on the trip are kept.

With a session, an existing document (a reference, a bare id, or an inline document whose id exists) is attached as stored, and only when it is a document of your platform; the body never rewrites it. A new inline document may only point at a file of your platform, which is linked as stored.

When a non-blank shareCode query parameter is sent, the call is authorized by the trip's share settings instead of a session (the share must be open and the code must match); the trip is then looked up regardless of platform and the request acts on the trip's own platform. A share link may only add NEW documents around files just uploaded for the trip (POST /file/upload with the share code) that back no document yet.

Permission: update:trip, or a valid shareCode query parameter

Errors: 400 when the body is malformed, or names a file that is not yours or (share code) an existing document or a file that already backs one (document_attach_invalid); 401 when the share code does not match an open, unexpired share of the trip; 404 when no trip with this id exists (on your platform, or at all when a share code is used) or a referenced document is not one of your platform.

## CarrierPortal

The carrier portal (`customerPortalSettings.carrierPortal.enabled`) lets a subcontractor's `supplier` accounts work on the forwarded trips whose capacity contract is with their business: list and open them, start and finish them, start and complete stops (with questionnaires and photos), report arrival estimates, driver and vehicle details, attach POD/CMR documents and read the trip's eCMRs. Execution reuses the trip endpoints of the driver share link behind a carrier guard; every other trip answers `404`, and trips are served without prices, cost lines, quotes or the customers' orders. What carriers may do is set in `customerPortalSettings.carrierPortal`.

### Get a carrier trip

 - [GET /carrier/trip/{id}](https://docs.transportial.com/apis/carrierportal/get:carrier:trip.md): One forwarded trip of the caller's business with its stops, consignments and documents, the chats the carrier can open for it and what the caller may do with it. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused.

Permission: get:carrier:trips

Errors: 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's.

### Get the pre-trip check status of a carrier trip

 - [GET /carrier/trip/{id}/startCheck](https://docs.transportial.com/apis/carrierportal/get:carrier:trip:startcheck.md): Whether the pre-trip check is required before this trip can start, exactly like GET /trip/{id}/startCheck.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused.

Permission: get:carrier:trips

Errors: 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's.

### Submit the pre-trip check of a carrier trip

 - [POST /carrier/trip/{id}/startCheck](https://docs.transportial.com/apis/carrierportal/create:carrier:trip:startcheck.md): Submits the pre-trip check, exactly like POST /trip/{id}/startCheck. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 when the answers are invalid or execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's.

### Attach photos to a stop of a carrier trip

 - [PUT /carrier/trip/{id}/action](https://docs.transportial.com/apis/carrierportal/update:carrier:trip:action:documents.md): Adds documents (photos taken at the stop) to one action of the trip. Unlike PUT /trip/{id}/action nothing else of the action changes. Each new document must reference a file uploaded with POST /file/upload that is not attached to any document yet. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 carrier_portal_document_invalid when a file is unknown or already attached, or execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's or the action is not on it.

### List the eCMRs of a carrier trip

 - [GET /carrier/trip/{id}/consignmentNotes](https://docs.transportial.com/apis/carrierportal/get:carrier:trip:consignmentnotes.md): The non-cancelled eCMRs of the consignments on the trip, exactly like GET /trip/{id}/consignmentNotes. Only available with the ecmr feature.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused.

Permission: get:carrier:trips

Errors: 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's or the ecmr feature is off.

### List the carrier's trips

 - [GET /carrier/trips/{offset}/{amount}](https://docs.transportial.com/apis/carrierportal/get:carrier:trips.md): Lists the forwarded trips the caller's business executes, newest start first, without pricing. Isolation is applied in the query itself: the trips of other carriers and other platforms are never selected.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Trips in a status the platform hides from carriers are left out. A shareCode query parameter is refused.

Permission: get:carrier:trips

Errors: 401 when the caller is not a supplier account or the feature is off.

### Report the arrival estimate of a stop

 - [PUT /carrier/trip/{id}/action/{actionId}/eta](https://docs.transportial.com/apis/carrierportal/update:carrier:trip:action:eta.md): Stores the carrier's arrival estimate for one stop in the trip's executorDetails.stopEtas (which route recalculation never overwrites) and sets it as the stop's eta. Queues afterUpdatingTrip, so message automations can pass the new estimate on. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 when the body is malformed or execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's or the action is not on it.

### Record unplanned work on a carrier trip

 - [POST /carrier/trip/{id}/action:unplanned](https://docs.transportial.com/apis/carrierportal/create:carrier:trip:action:unplanned.md): Records work nobody planned (a break, refuel, weighing, customs check), exactly like POST /trip/{id}/action:unplanned. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 when the body is malformed or execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's.

### Report the position of a carrier trip

 - [POST /carrier/trip/{id}/locationUpdate](https://docs.transportial.com/apis/carrierportal/create:carrier:trip:locationupdate.md): Records a browser GPS position for the trip's vehicle, exactly like POST /trip/{id}/locationUpdate (ETA recalculation, stop progression).

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 when the body is malformed or execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's or has no vehicle.

### Download an eCMR PDF of a carrier trip

 - [GET /carrier/trip/{id}/consignmentNote/{noteId}/pdf](https://docs.transportial.com/apis/carrierportal/get:carrier:trip:consignmentnote:pdf.md): The sealed eCMR PDF of a consignment on the trip, exactly like GET /trip/{id}/consignmentNote/{noteId}/pdf. Only available with the ecmr feature.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused.

Permission: get:carrier:trips

Errors: 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's, the ecmr feature is off, or the eCMR's consignment is not on the trip.

### Attach POD, CMR or other documents to a carrier trip

 - [PUT /carrier/trip/{id}/documents](https://docs.transportial.com/apis/carrierportal/update:carrier:trip:documents.md): Adds documents to the trip, one per file uploaded with POST /file/upload. Files that are already attached to a document and references to existing documents are refused. Queues afterUpdatingTrip. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 carrier_portal_document_invalid when the list is empty, a file is unknown or already attached, or execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's.

### Start or complete several actions of a carrier trip

 - [PUT /carrier/trip/{id}/actions/lifecycle](https://docs.transportial.com/apis/carrierportal/update:carrier:trip:actions:lifecycle.md): Moves several actions of the trip through the same life cycle step in one call, exactly like PUT /trip/{id}/actions/lifecycle. Ids that are not on the trip are skipped. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 when the body is malformed or execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's or an action is hidden from the carrier.

### Start or complete a stop of a carrier trip

 - [PUT /carrier/trip/{id}/action/{actionId}](https://docs.transportial.com/apis/carrierportal/update:carrier:trip:action:lifecycle.md): Moves one action of the trip to a life cycle, exactly like PUT /trip/{id}/action/{actionId}: ACTUAL starts it, REALIZED stores the result (questionnaire answers, photos) and ends it. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 when the body is malformed or execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's or the action is not on it.

### Report driver and vehicle of a carrier trip

 - [PUT /carrier/trip/{id}/executor](https://docs.transportial.com/apis/carrierportal/update:carrier:trip:executor.md): Stores who executes the trip in the trip's executorDetails (free text, any transport mode). Reported arrival estimates are kept. Queues afterUpdatingTrip. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_executor_details_disabled when the platform does not let carriers report these details (customerPortalSettings.carrierPortal.allowExecutorDetails).

Permission: update:carrier:trip

Errors: 400 when a text is too long (carrier_portal_executor_details_invalid) or reporting is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's.

### Finish a carrier trip

 - [GET /carrier/trip/{id}/finish](https://docs.transportial.com/apis/carrierportal/finish:carrier:trip.md): Finishes the trip exactly like the driver execution page does (GET /trip/{id}/finish). The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 when execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's.

### Start a carrier trip

 - [GET /carrier/trip/{id}/start](https://docs.transportial.com/apis/carrierportal/start:carrier:trip.md): Starts the trip exactly like the driver execution page does (GET /trip/{id}/start), including the pre-trip check gate. The trip is returned stripped for the carrier: no prices, cost lines, quotes, customer orders or actions hidden from the executor.

Callers must be a supplier account of a platform with the carrier portal enabled (customerPortalSettings.carrierPortal.enabled). Only forwarded trips whose capacity contract is with one of the caller's businesses are reachable; any other trip id answers 404, as does a trip in a status the platform hides from carriers (customerPortalSettings.carrierPortal.hiddenTripStatuses). A shareCode query parameter is refused. Refused with 400 carrier_portal_execution_disabled when the platform does not let carriers execute trips (customerPortalSettings.carrierPortal.allowExecution).

Permission: update:carrier:trip

Errors: 400 when the pre-trip check is required or failed (trip_start_check_*) or execution is disabled; 401 when the caller is not a supplier account or the feature is off; 404 when the trip is not one of the caller's.

## TransportialNetwork

The Transportial network connects a shipper and a carrier that each work on their own Transportial platform. The shipper invites a carrier business; the carrier accepts the one-time code on its platform, choosing scopes (`orders`, `status`, `documents`, `invoices`) and what happens with changes once it started executing. A trip forwarded under a capacity contract with communication method `transportial` then becomes a transport order on the carrier's platform, with updates and cancellation; the carrier's execution (stops, arrival estimates, driver and vehicle, PODs, CMRs, eCMR numbers) flows back to the forwarded trip, and its invoices arrive in the shipper's incoming invoices matched to the trip. Entities are mapped through link-scoped keys, never through the other platform's ids; either side can revoke the link. Each side sees the sync log of every network order.

### List Transportial network links

 - [GET /transportial-network/links](https://docs.transportial.com/apis/transportialnetwork/get:transportial-network-links.md): Lists your platform's Transportial network links, newest first: as shipper the carriers you forward trips to on their own Transportial platform, as carrier the shippers whose orders you receive. Pending invites, active and revoked links are included.

Permission: get:transportialNetwork.

### Invite a carrier to the Transportial network

 - [POST /transportial-network/links](https://docs.transportial.com/apis/transportialnetwork/create:transportial-network-invite.md): Creates a pending shipper link for one of your carrier businesses and returns a one-time inviteCode. Give the code to the carrier; it accepts it on its own Transportial platform with POST /transportial-network/links/accept. Nothing crosses between the platforms before that. A new invite for the same business replaces an unused one.

Permission: create:transportialNetworkLink.

Errors: 400 with transportial_network_already_linked (the business already has an active link) or transportial_network_scope_invalid; 404 when the business or administration is not on your platform.

### Update a Transportial network link

 - [PUT /transportial-network/link/{id}](https://docs.transportial.com/apis/transportialnetwork/update:transportial-network-link.md): Changes the note, narrows the scopes, or (as carrier) sets what happens with changes from the shipper after execution started (changesAfterStart: refuse, review, apply). Scopes can only be narrowed; a scope crosses only while both sides hold it.

Permission: update:transportialNetworkLink.

Errors: 400 with transportial_network_scope_invalid or transportial_network_policy_invalid; 404 when the link is not yours.

### Sync status of network orders

 - [GET /transportial-network/orders](https://docs.transportial.com/apis/transportialnetwork/get:transportial-network-orders.md): The network orders of one of your forwarded trips (tripId, shipper side) or received transport orders (transportOrderId, carrier side), with status, conflicts, the carrier's reported state and the sync log.

Permission: get:transportialNetwork.

Errors: 400 with transportial_network_orders_filter_required when neither filter is given; 404 when the trip or order is not yours.

### Apply or dismiss a held change

 - [POST /transportial-network/order/{id}/review](https://docs.transportial.com/apis/transportialnetwork/update:transportial-network-order-review.md): Carrier side: decides on a change or cancellation from the shipper that was held because execution had started and the link's changesAfterStart is review. apply applies it to your transport order (a cancellation cancels it); dismiss drops it and the shipper sees it was dismissed.

Permission: update:transportialNetworkOrder.

Errors: 400 with transportial_network_review_invalid or transportial_network_review_nothing_pending; 404 when the network order is not yours.

### Revoke a Transportial network link

 - [POST /transportial-network/link/{id}/revoke](https://docs.transportial.com/apis/transportialnetwork/update:transportial-network-link-revoke.md): Revokes the link on both platforms: no orders, updates, status, documents or invoices cross any more, and an invoice address set up for it is closed. Orders already exchanged stay on both sides. Revoking a pending invite makes its code unusable.

Permission: update:transportialNetworkLink.

Errors: 404 when the link is not yours.

### Sync a network order again

 - [POST /transportial-network/order/{id}/resync](https://docs.transportial.com/apis/transportialnetwork/update:transportial-network-order-resync.md): Shipper side: sends the current state of the forwarded trip again (for example after a failed delivery). Carrier side: pushes the current execution state to the shipper again. Runs after the request; check the sync log for the outcome.

Permission: update:transportialNetworkOrder.

Errors: 404 when the network order is not yours.

### Accept a Transportial network invite

 - [POST /transportial-network/links/accept](https://docs.transportial.com/apis/transportialnetwork/create:transportial-network-link-accept.md): Accepts a shipper's invite code on your (the carrier's) platform. Creates your carrier link, activates the shipper's link and from then on the shipper's forwarded trips under a contract with communication method transportial arrive as transport orders for businessId (or a new business named after the shipper). With the invoices scope an invoice address of the shipper is stored on that business so your invoices land in their inbox. Each code works once.

Permission: create:transportialNetworkLink.

Errors: 400 with transportial_network_invite_invalid (unknown, expired, used or own code; the same answer for every case), transportial_network_already_linked, transportial_network_scope_required or transportial_network_policy_invalid; 404 when businessId is not on your platform.

## TripOffer

A trip offer is a first-to-accept dispatch offer of one trip to one driver. A planner offers an unassigned trip to operational drivers whose vehicle was recently seen within the platform's first-to-accept radius of the first stop; each gets an expiring offer and an app notification. The first driver to accept is assigned (optionally with their preferred vehicle) and all other open offers become lost; accepts are serialised by a lock on the trip row. Offers can also be declined, withdrawn or expire (stamped lazily on read/accept), and offering requires first-to-accept to be enabled in the platform's planning settings.

### Decline a trip offer

 - [POST /trip-offer/{id}/decline](https://docs.transportial.com/apis/tripoffer/update:trip-offer:decline.md): The driver turns the offered trip down. The offer becomes DECLINED with respondedAt set to now; the offers of other drivers stay open and the trip is not changed.

Only the driver the offer was made to may decline it, unless the caller is a planner (role level 100 or higher).

Permission: update:trip

Errors: 400 when the offer is no longer OFFERED; 401 when the offer belongs to another driver and the caller is not a planner; 404 when no trip offer with this id exists on your platform.

### Offer a trip to nearby drivers

 - [POST /trip/{id}/offers:nearby](https://docs.transportial.com/apis/tripoffer/create:trip:offers:nearby.md): Starts (or extends) a first-to-accept round for an unassigned trip. Finds vehicles of your platform whose latest known position lies within the platform's first-to-accept radius (radiusKm) of the trip's first stop and is not older than locationMaxAgeHours, nearest first (at most 200 vehicles considered), and maps them to their operational drivers that have a user account.

For each such driver (up to maxDrivers) an OFFERED trip offer is created that expires after offerExpiryMinutes, and the driver receives an app notification linking to the trip. Expired offers of the trip are first stamped EXPIRED. The call is idempotent per driver: drivers that already hold an open or accepted offer for this trip, or that are already assigned to it, are skipped, so re-running only reaches drivers who came into range since. Offers are live entities, so the driver app sees them appear without polling.

Returns only the offers created by this call (possibly an empty list).

Permission: update:trip

Errors: 400 when first-to-accept dispatching is disabled for the platform, or when the trip's first stop has no point location; 404 when no trip with this id exists on your platform.

### List the offers of a trip

 - [GET /trip/{id}/offers](https://docs.transportial.com/apis/tripoffer/get:trip:offers.md): Returns every offer ever made for the trip, oldest first: which drivers were asked, and what each one answered (offered, accepted, declined, lost, expired, withdrawn). Use it as the planner's who-was-asked view.

Before listing, OFFERED offers whose expiresAt has passed are stamped EXPIRED (there is no background expiry job), so the list reflects what actually stands. Not paginated.

Permission: get:trip

Errors: 404 when no trip with this id exists on your platform.

### Accept a trip offer

 - [POST /trip-offer/{id}/accept](https://docs.transportial.com/apis/tripoffer/update:trip-offer:accept.md): The driver takes the offered trip. First accept wins: the trip row is locked pessimistically, so of two concurrent accepts (also across API replicas) exactly one succeeds.

On success the offer becomes ACCEPTED, every other OFFERED offer of the trip becomes LOST, the driver is added to the trip's chauffeurs, and when the platform setting assignPreferredVehicle is on and the trip has no vehicle, the driver's preferred vehicle is assigned. The trip is saved (triggering the usual trip save side effects) and the planner who started the round gets a notification.

Only the driver the offer was made to may accept it, unless the caller is a planner (role level 100 or higher).

Permission: update:trip

Errors: 400 when the offer is no longer OFFERED (already accepted, declined, lost or withdrawn) or has expired (it is then stamped EXPIRED); 401 when the offer belongs to another driver and the caller is not a planner; 404 when no trip offer with this id exists on your platform.

### Withdraw the open offers of a trip

 - [POST /trip/{id}/offers:withdraw](https://docs.transportial.com/apis/tripoffer/update:trip:offers:withdraw.md): Pulls a first-to-accept round back: expired offers are first stamped EXPIRED, then every offer of the trip that is still OFFERED flips to WITHDRAWN with respondedAt set to now, so no driver can accept it any more. Accepted, declined, lost and expired offers are left untouched; the trip's assignment is not changed.

Returns only the offers withdrawn by this call (empty when none were open).

Permission: update:trip

Errors: 404 when no trip with this id exists on your platform.

### List my open trip offers

 - [GET /trip-offers/open](https://docs.transportial.com/apis/tripoffer/get:trip-offers:open.md): Returns the offers the logged-in driver can still act on: status OFFERED and not yet expired, on the caller's driver platform, soonest to expire first. This is what the driver app shows on its trips screen. Each offer embeds the full trip so it can be rendered without a second fetch. Not paginated.

The caller is resolved to the chauffeur linked to the logged-in user on the active platform.

Permission: get:trip

Errors: 401 when there is no logged-in user; 404 when the logged-in user is not linked to a chauffeur on the active platform.

## Action

An action is one unit of work in transport execution: a stop, load, unload, move, coupling (attach/detach chauffeur or transport equipment), hand-over, break, wait, refuel, customs, weighing, booking or generic activity, selected by its `type`. Actions usually sit inside trips and consignments, nested at most one level below a stop (sub-actions inherit the stop's location). They can also be planned on their own against a vehicle, chauffeur or transport equipment; a scheduled sweep flips such pending planned actions to `actual` once their start time has passed, which performs the attach/detach. An action moves through the life cycles requested, planned, projected, actual and realized, and records its outcome in a result (status, reason, questionnaire answers, attachments).

### List pending planned actions of assets

 - [POST /planned-actions](https://docs.transportial.com/apis/action/get:planned-actions.md): Returns, in one call, the pending actions planned directly against a batch of vehicles, chauffeurs and/or transport equipments, for example to show upcoming attach/detach, break or refuel markers on a planboard.

An action is returned when it references ANY of the supplied ids, has life cycle planned, has a start time, and is neither archived nor deleted. Every action type is covered, not only attach/detach. The result is ordered by start time ascending and capped at 500 rows. Each row is a flat projection (PlannedActionModel) carrying the asset and location names, not the full action.

When all three id lists are empty (or contain only blank ids) an empty list is returned. Ids of other platforms never match.

Permission: get:actions

Errors: 400 when the body cannot be parsed.

### Edit a pending planned action

 - [PUT /planned-action/{id}](https://docs.transportial.com/apis/action/update:planned-action.md): Edits a pending action that was planned directly against a vehicle, chauffeur or transport equipment: its start time, name, duration, remark and location. This is a partial update: a field that is omitted or null is left unchanged, a blank name is ignored, and a blank locationId clears the location. A location id is resolved within your platform; an unknown id clears the location.

The type of the action and the assets it couples cannot be changed here; to change those, delete the action and plan a new one.

Permission: update:action

Errors: 400 when the action is not planned against an asset, or its life cycle is no longer planned; 404 when no action with this id exists on your platform.

### List actions

 - [GET /actions/{offset}/{amount}](https://docs.transportial.com/apis/action/get:actions.md): Returns a page of the actions of your platform (all action types, including sub-actions and actions inside trips), together with the total number of actions. Archived and deleted actions are excluded.

offset is the number of actions to skip and amount the page size. An amount above the server's maximum page size (1000 by default, configurable through MAX_PAGE_SIZE) is clamped to that maximum.

Permission: get:actions

Errors: 400 when offset or amount is not a number.

### Search actions

 - [POST /actions/{offset}/{amount}](https://docs.transportial.com/apis/action/search:actions.md): Full-text search over the actions of your platform. Archived and deleted actions are always excluded.

- query matches the action's search string (name, remark and related texts) as a phrase with a slop of 10, so the words must appear close together but not necessarily adjacent. A query without any letter or digit is ignored.
- sortBy is a list of sort instructions, each with fields (indexed field names) and direction (asc or desc, default desc). Without sortBy results are ordered by relevance.

offset is the number of hits to skip and amount the page size. The response carries the page of matching actions and the total hit count. When the search itself fails (for example a sort on a field that is not sortable) an empty page with a total of 0 is returned instead of an error.

Permission: get:actions

Errors: 400 when offset or amount is not a number or the body is not a JSON object.

### Submit the result of an action

 - [POST /action/{id}/result](https://docs.transportial.com/apis/action/submit:action:result.md): Stores the driver's questionnaire answers and attachments (photos, signatures, documents) as the result of an action.

Without a questionnaire (none configured or enabled for the action's type) the answers and attachments are stored on the action's result as sent, without validation, and the response reports success.

With a questionnaire the answers are first validated: every applicable required question must be answered, select answers must be one of the options, number, date/datetime and boolean answers must parse, and the question's length/range/decimals limits apply. Questions whose conditions do not hold are skipped. On a validation failure nothing is saved and the response has HTTP 200 with success: false and the reasons in errors.

When valid, the answers and attachments replace those on the result, and the result status/reason is set by the first matching result mapping, else by the first chosen option that sets a status, else to succeeded. Each answered question with a field mapping then writes to its target (action, consignment of a load/unload, or the result itself), records a discrepancy, or appends a comment, depending on its strategy. Photo attachments of questions mapped to action.documents become documents on the action. Discrepancies queue the AfterQuestionnaireConflict trigger.

The action is saved and the AfterActionLifeCycleUpdate integration trigger is queued only when processing succeeds; a mapping that cannot be applied makes the call return success: false with the cause in errors.

Permission: update:action

Errors: 400 when the body cannot be parsed; 404 when no action with this id exists on your platform, or the request has no platform context.

### Reschedule a pending planned action

 - [PUT /planned-action/{id}/time](https://docs.transportial.com/apis/action/update:planned-action-time.md): Moves a pending action that was planned directly against a vehicle, chauffeur or transport equipment to another moment by replacing its startTime. Nothing else changes; the scheduled sweep executes the action once the new start time has passed.

Unlike PUT /action/{id}/times, which edits a time window and drags the surrounding trip legs along, this endpoint moves a single instant and refuses actions that belong to a trip or have already been executed.

Permission: update:action

Errors: 400 when the action is not planned against an asset, or its life cycle is no longer planned; 404 when no action with this id exists on your platform.

### Change the life cycle of an action

 - [PUT /action/{id}/{lifeCycle}](https://docs.transportial.com/apis/action/update:action:lifecycle.md): Sets the life cycle of an action, for example to mark it as started (actual) or completed (realized). The new life cycle is applied recursively to all sub-actions, and a load, unload, handOver, attachTransportEquipment or detachTransportEquipment also passes it on to the stop that contains it. Capacity change events linked to the action follow along and are stamped with the current time when the life cycle becomes actual or realized.

An action that was cancelled for replanning only accepts realized; any other value leaves it unchanged. A value that is not one of the allowed life cycles is treated as planned.

Afterwards the trip containing the action (if any) is updated, and the AfterActionLifeCycleUpdate integration trigger is queued for integrations and workflows listening to it.

Permission: update:action

Errors: 404 when no action with this id exists on your platform.

### Update an action

 - [PUT /action](https://docs.transportial.com/apis/action/update:actions.md): Updates the action identified by the id in the body. The body is merged onto the stored action: fields present in the body overwrite the stored values (collections are replaced), read-only fields are ignored. The type discriminator selects the subclass and cannot be used to change the type of an existing action.

When the id does not exist on any platform the action is created instead. The updated action is saved with the same pre-save logic as on create (location propagation to sub-actions, nesting depth check).

For pending actions planned against an asset prefer PUT /planned-action/{id}, and for moving an action in time prefer PUT /action/{id}/times, which also keeps the surrounding trip legs consistent.

Permission: update:action

Errors: 400 when the body cannot be parsed or fails validation; 404 when the id belongs to an action of another platform.

### Create an action

 - [POST /action](https://docs.transportial.com/apis/action/create:action.md): Creates a single action from the request body. The concrete action subclass (load, unload, stop, move, break, attachChauffeur, ...) is chosen by the type discriminator, so the body may carry the type-specific fields of that subclass.

On save the action gets the platform of the caller, a containment UUID is derived for load/unload actions, and a parent action that has a location copies that location onto all of its sub-actions. Actions may be nested at most two levels deep (a stop with sub-actions); a deeper nesting is rejected.

Most actions are created as part of a trip or consignment; use this endpoint for stand-alone actions. To plan an action against vehicles, chauffeurs or transport equipment use POST /planned-actions/plan instead.

Permission: create:action

Errors: 400 when the body cannot be parsed (for example an unknown type) or fails validation.

### Get an action

 - [GET /action/{id}](https://docs.transportial.com/apis/action/get:action.md): Returns a single action of your platform by id, serialised as its concrete subclass (the type field tells which), including its sub-actions, location, constraints and result.

Archived and deleted actions are not returned.

Permission: get:action

Errors: 404 when no action with this id exists on your platform.

### Delete an action

 - [DELETE /action/{id}](https://docs.transportial.com/apis/action/delete:action.md): Soft-deletes an action: it is flagged deleted and no longer returned by lists, searches or lookups. The row itself is kept.

An action planned directly against a vehicle, chauffeur or transport equipment (not part of a trip) that has already been executed (life cycle actual or realized) cannot be deleted: its attach/detach has already happened and deleting the record would not undo it. Actions inside a trip can always be deleted here; their trip owns their life cycle.

Permission: delete:action

Errors: 400 when the action is an executed solo action planned against an asset; 404 when no action with this id exists on your platform.

### Get the result questionnaire of an action

 - [GET /action/{id}/result/questionnaire](https://docs.transportial.com/apis/action/get:action:result:questionnaire.md): Returns the questionnaire a driver has to fill in when completing this action. Questionnaires are configured per action type in the platform's planning settings (actionResultQuestionnaires); the one for the action's type is returned when it exists and is enabled.

When no enabled questionnaire is configured for the action's type the call still succeeds with HTTP 200, but success is false, message is "No questionnaire configured" and questionnaire is null.

Permission: read:action

Errors: 404 when no action with this id exists on your platform, or the request has no platform context.

### Generate action result questionnaires with AI

 - [POST /action/questionnaire/generate](https://docs.transportial.com/apis/action/generate:action:result:questionnaires.md): Creates or modifies the platform's action result questionnaires (the questions a driver answers when completing an action, per action type) from a natural-language instruction and/or uploaded documents, such as a board-computer information sheet.

The AI model selected for your platform receives the instruction, the documents and the configuration as currently edited (currentQuestionnaires, which may be unsaved). Its output is sanitised (unknown action types and unsupported field mappings are dropped, question ids are made unique) and merged into currentQuestionnaires: the action types the AI produced are replaced, every other entry is returned exactly as sent.

Nothing is saved. To apply the result, store actionResultQuestionnaires in the platform's planning settings.

Permission: update:platform

Errors: 400 when content is blank and none of the documents resolves to a file of your platform.

### Change the start and end time of an action

 - [PUT /action/{id}/times](https://docs.transportial.com/apis/action/update:action:times.md): Sets the scheduled startTime and endTime of an action and saves it. The same times are applied to all of its sub-actions, and for a load, unload or handOver also to the stop that contains it.

When the action is a stop on a trip, the surrounding move legs are kept consistent: the move arriving at the stop now ends at the new start time and the move leaving it starts at the new end time.

This edits the scheduled instants, not the planning time-window constraints of the action. For a pending action planned against an asset use PUT /planned-action/{id}/time instead.

Permission: update:action

Errors: 400 when the body cannot be parsed; 404 when no action with this id exists on your platform.

### Plan solo actions against assets

 - [POST /planned-actions/plan](https://docs.transportial.com/apis/action/plan:actions.md): Plans an action directly against one or more vehicles, chauffeurs and/or transport equipments, without it belonging to a trip. One action is created per supplied asset id, all sharing the same type, name, start time, duration, remark and location, so the same call plans a single action or the same action in bulk across a selection.

The actions are created with life cycle planned. A scheduled job flips each one to actual once its start time has passed (unless the platform disabled the planned-action sweep), which runs the same logic as executing the action by hand: an attachChauffeur/detachChauffeur or attachTransportEquipment/detachTransportEquipment then really couples or uncouples the asset.

Attach/detach types need both sides of the coupling: the subject asset from the id lists plus the counterpart in vehicleId, chauffeurId or transportEquipmentId. A handOver needs fromChauffeurId and toChauffeurId. All ids are resolved within your platform; unknown ids are ignored.

Permission: create:action

Errors: 400 when type is not a known action type, startTime is missing, none of the supplied asset ids resolves to an asset of your platform, or an attach/detach/handOver lacks one side of the coupling.

## Action Schedules

An action schedule is a named set of actions planned outside a trip, such as a driver swap (attach/detach chauffeur actions) or a maintenance visit, at a scheduled time and location. The server stores it with its actions, derives the vehicle and chauffeurs from the attach/detach chauffeur actions on every save, and soft-deletes it on delete. It does not execute schedules or change their status: the client maintains the status (PLANNED, IN_PROGRESS, COMPLETED, CANCELLED, FAILED).

### Get an action schedule

 - [GET /action-schedules/{id}](https://docs.transportial.com/apis/action-schedules/get:actionschedule.md): Returns one action schedule of your platform by id, with its actions, vehicle and chauffeurs.

Permission: read:action_schedule

Errors: 404 when no non-archived, non-deleted action schedule with this id exists on your platform.

### Delete an action schedule

 - [DELETE /action-schedules/{id}](https://docs.transportial.com/apis/action-schedules/delete:actionschedule.md): Soft-deletes an action schedule of your platform: it is marked deleted and no longer returned. Answers with HTTP 204 and no body.

Permission: delete:action_schedule

Errors: 404 when no non-archived, non-deleted action schedule with this id exists on your platform.

### Update an action schedule

 - [PUT /action-schedules](https://docs.transportial.com/apis/action-schedules/update:actionschedule.md): Updates an action schedule of your platform, for example its status, time or actions. The schedule is identified by the id in the body; fields present in the body overwrite the stored values and omitted fields keep theirs. vehicle and chauffeurs are derived again from the actions on save. An id that exists on no platform creates a new schedule.

Permission: update:action_schedule

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to another platform.

### Create an action schedule

 - [POST /action-schedules](https://docs.transportial.com/apis/action-schedules/create:actionschedule.md): Creates an action schedule on your platform: a named set of actions (for example a driver swap or a maintenance visit) at a scheduled time and location. The actions in the body are created with it. On save the server derives vehicle and chauffeurs from the attach/detach chauffeur actions. Nothing is executed automatically. Answers with HTTP 201.

Permission: create:action_schedule

Errors: 400 when the body is not valid JSON or does not match the schema.

### List action schedules

 - [GET /action-schedules/{offset}/{amount}](https://docs.transportial.com/apis/action-schedules/get:actionschedules.md): Lists the non-archived, non-deleted action schedules of your platform, most recently updated first. totalResults is the number of schedules on the platform.

Permission: read:action_schedule

## Event

Something that happened to a vehicle, trailer, sensor, stop or trip at a point in time: position fixes, ignition/motion/standstill changes, sensor readings, stop arrival/departure and gate moves, capacity changes, and free-form board-computer activities. Events are append-only facts, mostly written by integrations (Transics, FleetAccess, Squarell, Trimble, telemetry feeds, project44), sensors and the vehicle endpoints; `type` selects the subtype. Saving a location update can move a vehicle's or trailer's latest location and progress its trip, and every event can trigger integrations (AfterCreatingEvent and subtype triggers).

### Update an event

 - [PUT /event](https://docs.transportial.com/apis/event/update:event.md): Updates the event identified by the id in the body. Fields present in the body overwrite the stored values; omitted fields are kept. The subtype (type) cannot be changed. When no event with this id exists yet, a new one is created with it.

Permission: update:event

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to an event of another platform.

### Create an event

 - [POST /event](https://docs.transportial.com/apis/event/create:event.md): Creates an event of the subtype given by type (for example locationUpdateEvent, generalEvent, startEngineEvent) on your platform and returns it. Saving runs the subtype's side effects: a location update can become the latest location of its vehicle or trailer and progress the trip in transit, unless its dateTime is older than the platform's late-position threshold (planningSettings.locationTrackingSettings.latePositionThresholdMinutes) or than the stored latest location, in which case it is kept as track history only; integrations listening for AfterCreatingEvent (and the subtype-specific triggers such as AfterLocationUpdateEvent) are queued.

Most events are written by integrations and the vehicle endpoints; use this endpoint to push events from your own system.

Permission: create:event

Errors: 400 when the body is not valid JSON or type is missing or not one of the event subtypes.

### List events

 - [GET /events/{offset}/{amount}](https://docs.transportial.com/apis/event/create:events.md): Returns one page of the events of your platform (all subtypes), most recently updated first, with the total number of events. Deleted and archived events are excluded. There is no filtering: to read the events of one vehicle use the vehicle endpoints.

amount is not clamped on this route; the event table is very large, so keep pages small.

Permission: get:events

Errors: 400 when offset or amount is not an integer.

### Search events

 - [POST /events/{offset}/{amount}](https://docs.transportial.com/apis/event/search:events.md): Identical to GET /events/{offset}/{amount}: the request body is ignored and no search filters are applied. Returns one page of the events of your platform (all subtypes), most recently updated first, with the total number of events. Deleted and archived events are excluded. There is no filtering: to read the events of one vehicle use the vehicle endpoints.

amount is not clamped on this route; the event table is very large, so keep pages small.

Permission: get:events

Errors: 400 when offset or amount is not an integer.

### Get an event

 - [GET /event/{id}](https://docs.transportial.com/apis/event/get:event.md): Returns a single event of your platform, serialised as its concrete subtype (type), including its vehicle, trailer, sensor and position where the subtype has them.

Permission: get:event

Errors: 404 when no event with this id exists on your platform.

### Delete an event

 - [DELETE /event/{id}](https://docs.transportial.com/apis/event/delete:event.md): Soft-deletes an event: it is flagged as deleted and no longer returned by the event endpoints, but the row is kept. Returns the deleted event.

Permission: delete:event

Errors: 404 when no event with this id exists on your platform.

## Route

A route is a calculated path between a series of stops, with its total travel time (seconds), distance (metres) and cost, the move legs it consists of, toll expenses and the geometry as an encoded polyline. Every trip owns exactly one route, which is regenerated on trip save according to the platform's recalculation mode. The calculate endpoints run the platform's configured routing engine (PTV, PTV Developer, HERE, Google, OSRM or Transportial) for a vehicle, adding ADR restrictions and ISO 14083 / GLEC emission figures, and can compare alternatives from one or several engines; calculated routes are not stored. The CRUD endpoints store and manage route records directly.

### Calculate a route through coordinates

 - [POST /route/calculate:georeferences](https://docs.transportial.com/apis/route/calculate:route:georeferences.md): Calculates a route for a vehicle through a list of coordinates, in the order given. Only
latLonPointGeoReference entries are routed; entries without a type are inferred from their shape and
entries without usable coordinates are dropped. The departure time is taken from a
StartDateTimeConstraint in constraints (default now); dangerousGoods and tunnelRestrictionCode
add ADR restrictions, and the user's planning settings are applied.

The route is calculated with the routing engine configured in the platform's routing settings (PTV by default) and is not stored; save it on a trip or with POST /route to keep it.

The response adds the emission figures to additionalInfo: CO2eWTW and CO2eTTW in kg (the engine's ISO 14083 CO2e when it returns one, otherwise estimated from the distance and the vehicle's fuel type and emission standard using the platform's emission settings) and an emissionReport with the per-leg ISO 14083 / GLEC report.

Permission: calculate:route

Errors: 400 when the body is malformed or route calculation is disabled in the platform's routing
settings (engine none).

### Get a route

 - [GET /route/{id}](https://docs.transportial.com/apis/route/get:route.md): Returns one route of your platform by id, including its full geometry (geoReference), encoded polyline,
move legs, toll expenses, actors and constraints. For map previews that only need the geometry use
GET /route/{id}/lineString.

Permission: get:route

Errors: 404 when no route with this id exists on your platform.

### Delete a route

 - [DELETE /route/{id}](https://docs.transportial.com/apis/route/delete:route.md): Permanently deletes a route of your platform together with its move legs, expenses, actors, constraints
and identifiers, and returns the route as it was before deletion.

Permission: delete:route

Errors: 404 when no route with this id exists on your platform.

### List routes

 - [GET /routes/{offset}/{amount}](https://docs.transportial.com/apis/route/get:routes.md): Returns one page of the routes of your platform, plus the total number of routes. Use
POST /routes/{offset}/{amount} to search and sort.

Permission: get:routes

Errors: 400 when offset is not a number.

### Search routes

 - [POST /routes/{offset}/{amount}](https://docs.transportial.com/apis/route/search:routes.md): Searches the routes of your platform and returns one page of matches plus the total number of matches.

The body is a route search filter: query is a free-text phrase matched against the route's search
string, sortBy a list of { "fields": [...], "direction": "asc" | "desc" } sort instructions. Without
sortBy results are ranked by relevance. When the search itself fails (for example on a field that is not
sortable) the error is logged and an empty page with total 0 is returned.

Permission: get:routes

Errors: 400 when the body is not valid JSON or offset is not a number.

### Update a route

 - [PUT /route](https://docs.transportial.com/apis/route/update:route.md): Updates the route identified by the id in the body: the fields sent are merged into the stored route and
the result is saved. When no route with that id exists yet, a new route is created with it. Nothing is
recalculated.

Permission: update:route

Errors: 400 when the body is malformed or a value cannot be parsed; 404 when the id belongs to a
route of another platform.

### Create a route

 - [POST /route](https://docs.transportial.com/apis/route/create:route.md): Stores a route exactly as sent, together with its move legs, expenses, actors and constraints. Nothing is
calculated: use POST /route/calculate or POST /route/calculate:georeferences to have the routing
engine produce a route first.

Permission: create:route

Errors: 400 when the body is malformed or a value cannot be parsed.

### Calculate alternative routes

 - [POST /route/calculate:alternatives](https://docs.transportial.com/apis/route/calculate:route:alternatives.md): Calculates several candidate routes for a vehicle through a list of coordinates so they can be compared on
toll, travel time and emissions. The input is the same as for POST /route/calculate:georeferences.

How the candidates are produced follows the platform's routing settings: in single_engine mode the
configured engine returns its own variants (for example fastest and avoid-motorway or eco variants); in
multi_engine mode every engine listed in the alternative engines calculates the route once (an engine
that fails is skipped). Each candidate carries its label, engine, toll total and CO2e figures; the routes
are not stored.

Permission: calculate:route

Errors: 400 when the body is malformed or route calculation is disabled in the platform's routing
settings (engine none).

### Get the geometry of a route

 - [GET /route/{id}/lineString](https://docs.transportial.com/apis/route/get:route:linestring.md): Returns only the id and the encoded polyline of a route. Lean trip and vehicle board rows leave the route
geometry out, so map previews on those rows load it here when they open.

Permission: get:route

Errors: 404 when no route with this id exists on your platform.

### Calculate a route along actions

 - [POST /route/calculate](https://docs.transportial.com/apis/route/calculate:route.md): Calculates a route for a vehicle along a list of actions (stops), visited in ascending tripSequenceNr
order. Stop locations may be coordinates, lines or addresses (addresses are geocoded). When a stop is a
booking action, a booking-aware calculation is used. The departure time is taken from a
StartDateTimeConstraint in constraints (default now); dangerousGoods and tunnelRestrictionCode
add ADR restrictions, and the user's planning settings are applied.

The route is calculated with the routing engine configured in the platform's routing settings (PTV by default) and is not stored; save it on a trip or with POST /route to keep it.

The response adds the emission figures to additionalInfo: CO2eWTW and CO2eTTW in kg (the engine's ISO 14083 CO2e when it returns one, otherwise estimated from the distance and the vehicle's fuel type and emission standard using the platform's emission settings) and an emissionReport with the per-leg ISO 14083 / GLEC report.

Permission: calculate:route

Errors: 400 when the body is malformed or route calculation is disabled in the platform's routing
settings (engine none).

## Routing

Transportial's own multi-modal routing engine, which picks road, maritime, inland waterway, air or rail routing from the vehicle's transport mode and applies the platform's per-mode constraints (sea passages, barge draught and water levels, rail speed). API instances that do not run the engine hand the request to a routing-engine instance over Kafka and wait for its reply. The result is returned as a route with legs and an encoded polyline, without being stored as a route.

### Calculate a multi-modal route

 - [POST /routing/calculate/route](https://docs.transportial.com/apis/routing/calculate:routing:route.md): Calculates a route through a list of locations with Transportial's own multi-modal routing engine, choosing
the mode from the transport mode of the first vehicle in vehicles (road by default, or maritime, inland
waterway, air or rail, each honouring the platform's routing constraints for that mode). On API instances
that do not run the routing engine themselves, the request is stored as a routing request, handed to a
routing-engine instance over Kafka and the reply is awaited. The route is not stored as a route; the
response carries the encoded polyline in lineString.

Permission: calculate:routing:route

Errors: 400 when the body is malformed, the routing engine cannot be reached or reports a failure;
404 when no route could be produced.

## Optimization

Vehicle-routing optimization of trips: planning trips from consignments, vehicles and drivers, inserting a consignment into existing trips, re-optimizing trips, simulating scenarios and reviewing past optimizer runs. These endpoints are currently placeholders that do not run the optimizer: they return fixed confirmation messages, and trip planning currently fails with HTTP 500.

### Re-optimize existing trips

 - [POST /optimization/reoptimize](https://docs.transportial.com/apis/optimization/reoptimize:trips.md): Intended to re-plan a set of existing trips together to reduce their number, distance or cost.

Not yet implemented: the endpoint does not run the optimizer and answers with a fixed confirmation message (success: true). The request body is ignored.

Permission: optimization:reoptimize (and a permission level of at least 100)

Errors: none specific.

### Simulate an optimization

 - [POST /optimization/simulate](https://docs.transportial.com/apis/optimization/simulate:optimization.md): Intended to compute an optimized plan without saving the trips, to compare scenarios.

Not yet implemented: the endpoint does not run the optimizer and answers with a fixed confirmation message (success: true). The request body is ignored.

Permission: optimization:simulate (and a permission level of at least 50)

Errors: none specific.

### List optimization runs

 - [GET /optimization/runs](https://docs.transportial.com/apis/optimization/get:optimization-runs.md): Intended to list stored optimizer runs for analytics.

Not yet implemented: answers with a fixed message and an empty runs array; the query parameters are ignored.

Permission: optimization:view (and a permission level of at least 10)

Errors: none specific.

### Get an optimization run

 - [GET /optimization/runs/{id}](https://docs.transportial.com/apis/optimization/get:optimization-run.md): Intended to return one stored optimizer run with its input, solution and metrics.

Not yet implemented: the endpoint does not run the optimizer and answers with a fixed confirmation message (success: true). The id is not looked up.

Permission: optimization:view (and a permission level of at least 10)

Errors: none specific.

### Plan optimal trips

 - [POST /optimization/plan](https://docs.transportial.com/apis/optimization/plan:trips.md): Intended to build optimal trips from consignments, vehicles and drivers with the vehicle-routing optimizer, minimizing the chosen objective.

Not yet implemented: the optimizer is not called. On success the response is a simulation with no trips and zero metrics; in the current setup the handler fails with HTTP 500 ({"success": false, "message": ...}) because the database session it expects is not provided to it.

Permission: optimization:plan (and a permission level of at least 100)

Errors: 500 when planning fails.

### Insert a consignment into existing trips

 - [POST /optimization/insert-consignment](https://docs.transportial.com/apis/optimization/insert:consignment.md): Intended to find the cheapest position for a new consignment in the existing trips, or report that it needs a new trip.

Not yet implemented: the endpoint does not run the optimizer and answers with a fixed confirmation message (success: true). The request body is ignored.

Permission: optimization:insert (and a permission level of at least 50)

Errors: none specific.

## Recommendation

The recommendation engine ranks vehicles, chauffeurs, transport equipment, capacity contracts or existing trips for a consignment, and suggests return loads for a trip's vehicle from open tenders and connected freight exchanges. Each candidate gets a score from 0 to 1 built from weighted rules, with the reasons behind it and any warnings; a violated hard requirement gives a blocking warning and a score of 0, but the candidate is still returned. Both endpoints need the `recommendations` feature, have no side effects, and are tuned per platform (marketplace settings, optional Jev re-ranking).

### Suggest return loads for a trip

 - [GET /recommendations/returnLoad/forTrip/{tripId}](https://docs.transportial.com/apis/recommendation/get:returnloadsfortrip.md): Searches the platform's enabled freight exchanges, and the open tenders it already holds, for loads the trip's vehicle could take on its way back: from where the trip drops its last load to where the vehicle has to be next (its following planned trip, or the nearest of the operator's own locations).

Each suggestion carries its score, the reasoning behind it, and the legs the decision turns on: the empty kilometres to the pickup, the loaded kilometres and the kilometres onward to the next commitment, against the baseline of driving there empty. Loads blocked by a hard limit (too far, missed pickup or deadline, over capacity) are still returned with a blocking warning and a score of 0. A board offer that was already imported as a tender appears once, as the tender. Scoring is tuned per platform through its marketplace settings. The call has no side effects.

sources reports how each board answered, including boards that failed and criteria a board could not filter on itself. When the platform does not have the recommendations feature, the response is successful with no loads.

Permission: get:recommendation

Errors: 404 when no trip with this id exists on your platform (unknown, deleted or owned by another platform).

### Find trips that can still take a consignment

 - [GET /nearby/forConsignment/{consignmentId}](https://docs.transportial.com/apis/recommendation/get:nearbytripsforconsignment.md): Lists the platform's open trips (not draft, cancelled, pending completion or completed) that run within the time span of the consignment's windows (widened by nearbyPlanningSettings.timeHorizonHours) and whose remaining route passes within the radius of the consignment's pickup.

For each trip the cheapest place to insert the pickup and delivery is chosen, with the extra driving, the capacity left at that point of the route (weight, load metres, volume, pallet places, TEU — per transport mode), time-window compatibility and the trip resource checks (status, ADR, own customer as carrier). Feasible trips come first, ranked by score; trips that do not fit follow with the blocking reason unless the platform turns that off. Trips that already carry the consignment are left out. The call has no side effects.

When the platform does not have the recommendations feature the response is successful, enabled is false and nothing is searched.

Permission: get:nearbyPlanning (or get:recommendation)

Errors: 404 when no consignment with this id exists on your platform.

### Find unplanned consignments near a trip

 - [GET /nearby/forTrip/{tripId}](https://docs.transportial.com/apis/recommendation/get:nearbyconsignmentsfortrip.md): Lists the platform's unplanned consignments (statuses from nearbyPlanningSettings.consignmentStatuses, not on hold, not yet planned to their last stop) whose pickup lies within the radius of one stop (stopId), of the trip's last stop (afterLastStop=true, the next order after the trip) or of the trip's remaining route (default).

For each candidate the cheapest place to insert its pickup and delivery is chosen, and the answer says how much extra driving that costs (straight line × the platform road factor, or routed for the best few when configured), whether it fits the trip's remaining capacity at that point of the route (weight, load metres, volume, pallet places, TEU — per transport mode), and whether its time windows fit the trip's planned times. Trip resource checks (ADR, own customer as carrier) are added as warnings. Feasible candidates come first, ranked by score; candidates that do not fit are returned after them with the blocking reason unless the platform turns that off. The call has no side effects.

When the platform does not have the recommendations feature the response is successful, enabled is false and nothing is searched.

Permission: get:nearbyPlanning (or get:recommendation)

Errors: 404 when no trip with this id exists on your platform; 400 when stopId is not a stop of the trip.

### Recommend candidates for a consignment

 - [GET /recommendations/{candidateType}/forConsignment/{consignmentId}](https://docs.transportial.com/apis/recommendation/get:recommendationsforconsignment.md): Ranks candidates of one type (vehicles, chauffeurs, transport equipment, capacity contracts or existing trips) for a consignment and returns the best ones with the reasons behind each score and any planner warnings.

Up to 200 candidates of your platform are scored by weighted rules (availability in the consignment's time window, distance, capacity, and the vehicle type, fuel type, emission standard and equipment type constraints on the consignment and its goods). A violated hard requirement adds a blocking warning and sets the score to 0; such candidates are still returned so near misses stay visible. When the platform has a Jev re-ranker configured, it may re-order the shortlist. Results are sorted by score, highest first. The call has no side effects.

When the platform does not have the recommendations feature, the response is successful with an empty list.

Permission: get:recommendation

Errors: 404 when candidateType is not a known candidate type, or (with the feature enabled) when no consignment with this id exists on your platform (unknown, deleted or owned by another platform).

## StackMap

A stack map is the stop-by-stop load plan of a trip: a list of stack snapshots showing how goods are stacked in the vehicle and its transport equipment after each stop. It is calculated from the trip's stop actions by the built-in Transportial stacking engine, taking into account each capacitor's last load from earlier trips, and is linked to the trip. Calculation does not save anything; stack maps can be stored, listed, searched and deleted separately.

### Get a stack map

 - [GET /stack-map/{id}](https://docs.transportial.com/apis/stackmap/get:stackmap.md): Returns one stack map of your platform by id, with its stack snapshots.

Permission: get:stackMap

Errors: 404 when no stack map with this id exists on your platform.

### Delete a stack map

 - [DELETE /stack-map/{id}](https://docs.transportial.com/apis/stackmap/delete:stackmap.md): Permanently deletes a stack map of your platform, including its stack snapshots, and returns the deleted stack map.

Permission: delete:stackMap

Errors: 404 when no stack map with this id exists on your platform.

### Calculate a stack map from trip actions

 - [POST /stack-map/calculate](https://docs.transportial.com/apis/stackmap/calculate:stackmap.md): Calculates how the goods of a trip are stacked in the vehicle and its transport equipment, stop by stop, and returns the resulting stack map. The calculation is not saved by this endpoint.

For every stop action (in sequenceNr order) the built-in Transportial stacking calculator places the goods loaded so far into the vehicle's capacitors, taking into account the last stack plan of each capacitor from earlier trips (ignoring draft, requested and cancelled trips and the trip given in tripID). When a previous stackMap is sent, it is recalculated in place: its id is kept and snapshots of the same stop actions are updated.

Permission: calculate:stackMap

Errors: 400 when the body is not valid JSON or the calculation fails.

### List stack maps

 - [GET /stack-maps/{offset}/{amount}](https://docs.transportial.com/apis/stackmap/get:stackmaps.md): Returns a page of stack maps of your platform.

Paging uses offset and amount; totalResults holds the total number of stack maps.

Permission: get:stackMaps

Errors: 400 when offset or amount is not a number.

### Search stack maps

 - [POST /stack-maps/{offset}/{amount}](https://docs.transportial.com/apis/stackmap/search:stackmaps.md): Searches stack maps of your platform in the search index with an optional phrase query and sortBy instructions.

Paging uses offset and amount; totalResults holds the total number of matches. Unsortable sort fields do not fail the request but return an empty page.

Permission: get:stackMaps

Errors: 400 when the body is not valid JSON or offset/amount is not a number.

### Update a stack map

 - [PUT /stack-map](https://docs.transportial.com/apis/stackmap/update:stackmap.md): Updates a stack map identified by the id in the body. Only the fields present in the body are changed. When the id is unknown on your platform a new stack map is created with the sent fields.

Permission: update:stackMap

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a stack map of another platform.

### Create a stack map

 - [POST /stack-map](https://docs.transportial.com/apis/stackmap/create:stackmap.md): Creates a stack map on your platform. Stack snapshots are normally produced by POST /stack-map/calculate and linked through the trip; this endpoint stores the stack map as sent.

Permission: create:stackMap

Errors: 400 when the body is not valid JSON or a field has the wrong type.

## Map planning

Lightweight, read-only projections for the map planner: consignments as map points, trips as map lines and vehicles with position and availability, each about 250 bytes per row instead of a full entity. Each endpoint takes the entity's normal search body (same filters and visibility rules) plus a row `limit` (default 5000, max 10000) and, for trips and vehicles, a planning window. Vehicle rows say when and where the vehicle becomes free, based on its open trips in the window. Fetch the full entity through its own endpoints when a row is opened.

### Search consignments as map points

 - [POST /mapPlanning/consignments](https://docs.transportial.com/apis/map-planning/search:mapplanning:consignments.md): Returns the consignments matching a consignment search as lightweight map rows, built for drawing and planning thousands of orders at once: id, name, number, type, status, combined flag, transport order, customer, current position, the top-level goods lines (type, quantity, weight, load metres) and every stop of the consignment, its goods and contained goods with coordinates, kind (load, unload, mixed or stop) and time window, ordered by sequence number. Fetch a consignment through the consignment endpoints for the full entity.

The body is the consignment search object (same filters, sorting and visibility rules as the consignment search endpoints) plus limit; windowStart/windowEnd are accepted and ignored. Rows are returned in the order the search index ranked them. Archived and deleted consignments that the index still lists are left out.

Paging: there is no offset. At most limit rows are returned (default 5000, clamped to 1..10000). When the search matched more rows than were fetched, totalResults is the index's match count, so totalResults > limit means the map shows a subset; otherwise it is the number of rows returned.

Permission: get:consignments

Errors: 400 when the body is not valid JSON.

### Search vehicles with position and availability

 - [POST /mapPlanning/vehicles](https://docs.transportial.com/apis/map-planning/search:mapplanning:vehicles.md): Returns the vehicles matching a vehicle search as lightweight map rows: name, license plate, vehicle type, operational flag, last known position with heading, speed and time, capacity (maximum weight, load metres), assigned drivers, the trip the vehicle is driving now, and when and where it is free within the planning window.

Availability comes from the vehicle's open trips (status requested, accepted, confirmed, in_transit or modified) that overlap the window: trips in transit, plus trips that start between windowStart minus 7 days and windowEnd and are expected to end (the latest of the estimated end, the planned end, the stops' ETAs and end times, and the start date) on or after windowStart. openTripIds lists them, freeFrom is the expected end of the trip that ends last and freeAt the last stop of that trip that has coordinates. Both are null when the vehicle has no open trip in the window.

The body is the vehicle search object plus windowStart, windowEnd and limit; the window defaults to the current UTC day. Archived and deleted vehicles that the index still lists are left out.

Paging: there is no offset. At most limit rows are returned (default 5000, clamped to 1..10000). When the search matched more rows than were fetched, totalResults is the index's match count, so totalResults > limit means the map shows a subset; otherwise it is the number of rows returned.

Permission: get:vehicles

Errors: 400 when the body is not valid JSON, windowStart or windowEnd is not an ISO-8601 instant, or windowEnd is before windowStart.

### Search trips in a planning window as map lines

 - [POST /mapPlanning/trips](https://docs.transportial.com/apis/map-planning/search:mapplanning:trips.md): Returns the trips that overlap a planning window as lightweight map lines: name, status, plan status, start date, expected end, vehicle, drivers, route id, route distance (metres) and duration (seconds), the ids of the trip's consignments, and every stop (move legs left out) with coordinates, kind and time window, ordered by trip sequence. Route geometry is not included; load it through the route id for the one trip being inspected.

The body is the trip search object plus windowStart, windowEnd and limit. The window defaults to the current UTC day. The server replaces any date-range in the body with windowStart minus 7 days up to windowEnd (matched on the trip start date), and searches every status except cancelled unless the body sends statuses. Of these candidates it keeps trips that are in transit or whose expected end (the latest of the estimated end, the planned end and the stops' ETAs and end times, else the start date) is not before windowStart. Archived and deleted trips that the index still lists are left out.

Paging: there is no offset. At most limit rows are returned (default 5000, clamped to 1..10000). When the search matched more rows than were fetched, totalResults is the index's match count, so totalResults > limit means the map shows a subset; otherwise it is the number of rows returned.

Permission: get:trips

Errors: 400 when the body is not valid JSON, windowStart or windowEnd is not an ISO-8601 instant, or windowEnd is before windowStart.

## Constraint

Constraints are rules on how trips, consignments, goods, actions, locations and sensors may be planned or operated: time windows, earliest start and latest end, daily opening hours, vehicle, fuel, emission and equipment requirements, weight, size and speed limits, sensor thresholds, required attributes, routes and access restrictions, combined with and/or/not. They are polymorphic on `type` and are usually sent nested in the entity they constrain. Time constraints shape the delivery windows of PTV route optimisation, vehicle and equipment constraints block non-matching recommendation candidates, the opt-in `constraint_violated` trip check reports violations, and sensor value constraints raise threshold alerts. `enforceability` makes a time window soft (`preference`: tolerance, warnings, optimiser mapping per the platform timing settings) or keeps it hard (`enforced`, the default).

### List constraints

 - [GET /constraints/{offset}/{amount}](https://docs.transportial.com/apis/constraint/create:constraints.md): Lists all non-archived, non-deleted constraints of your platform, most recently updated first. The list contains every constraint row, including constraints that are nested in combinators or attached to trips, consignments, goods, actions, locations and sensors. totalResults is the number of constraints on the platform.

Permission: get:constraints

### Get a constraint

 - [GET /constraint/{id}](https://docs.transportial.com/apis/constraint/get:constraint.md): Returns one constraint of your platform by id, in the schema of its type. Combinators include their nested constraints.

Permission: get:constraint

Errors: 404 when no non-archived, non-deleted constraint with this id exists on your platform.

### Delete a constraint

 - [DELETE /constraint/{id}](https://docs.transportial.com/apis/constraint/delete:constraint.md): Permanently deletes a constraint of your platform. Nested constraints of a combinator are deleted with it. The response only carries success.

Permission: delete:constraint

Errors: 404 when no non-archived, non-deleted constraint with this id exists on your platform, or the request has no user session.

### Update a constraint

 - [PUT /constraint](https://docs.transportial.com/apis/constraint/update:constraint.md): Updates a constraint of your platform. The constraint is identified by the id in the body; fields present in the body overwrite the stored values and omitted fields keep theirs. Send the same type as the stored constraint: the type cannot be changed. An id that exists on no platform creates a new constraint.

Permission: update:constraint

Errors: 400 when the body is not valid JSON, has no or an unknown type, or fails validation; 404 when the id belongs to another platform.

### Create a constraint

 - [POST /constraint](https://docs.transportial.com/apis/constraint/create:constraint.md): Creates a standalone constraint on your platform. The body is polymorphic on type, which is required and selects the schema with the type-specific fields (see the Constraint schema for what each type does and where it is enforced). Nested constraints of andConstraint, orConstraint and notConstraint are created with it.

To constrain a specific trip, consignment, goods, action, location or sensor, the constraint is usually sent nested in that entity's constraints instead.

Permission: create:constraint or update:constraint

Errors: 400 when the body is not valid JSON, has no or an unknown type, or does not match the schema of its type.

## Capacity

A capacity is transport capacity offered by a business: cargo space, vehicles, transport equipment or chauffeurs, expressed as a number of units of a given size (capacityPerUnit x totalUnits) with a transport mode, an availability schedule and pricing modules. The total and available capacity are calculated when the capacity is created. Capacity is bought through one-time and permanent capacity contracts, which cannot buy more units than the capacity offers.

### Get a capacity

 - [GET /capacity/{id}](https://docs.transportial.com/apis/capacity/get:capacity.md): Returns one capacity of your platform with its schedule, pricing modules and identifiers.

Permission: get:capacity

Errors: 404 when no capacity with this id exists on your platform.

### Update a capacity

 - [PUT /capacity/{id}](https://docs.transportial.com/apis/capacity/update:capacity.md): Updates the capacity identified by the id in the body; fields left out keep their value. Unlike
create, totalCapacity and availableCapacity are not recalculated: the values sent are stored as they are.
When no capacity with that id exists on your platform, a new capacity is created with it.

Permission: update:capacity

Errors: 400 when the body is not valid JSON; 404 when the id belongs to a capacity of another platform.

### Delete a capacity

 - [DELETE /capacity/{id}](https://docs.transportial.com/apis/capacity/delete:capacity.md): Permanently deletes a capacity of your platform together with its schedule, pricing modules and
identifiers, and returns the capacity as it was before deletion.

Permission: delete:capacity, or create:capacity (accepted until roles carry delete:capacity)

Errors: 404 when no capacity with this id exists on your platform.

### Create a capacity

 - [POST /capacity](https://docs.transportial.com/apis/capacity/create:capacity.md): Creates a capacity: transport capacity (loads, vehicles, transport equipment or chauffeurs) that a
business offers, which can then be bought through capacity contracts.

The server calculates totalCapacity as capacityPerUnit times totalUnits (with the unit of
capacityPerUnit; a comma is accepted as decimal separator) and sets availableCapacity to the same value;
values sent for these two fields are ignored. A nested schedule and the pricingModules are saved together
with the capacity.

Permission: create:capacity

Errors: 400 when the body is not valid JSON or a pricing module has a missing or unknown type.

### List capacities

 - [GET /capacities/{offset}/{amount}](https://docs.transportial.com/apis/capacity/get:capacities.md): Returns one page of the capacities of your platform, most recently updated first. Archived and deleted
capacities are left out. totalResults is the number of capacities across all pages.

Permission: get:capacities

Errors: 400 when offset is not a number.

## CapacityContract

A capacity contract is an agreement with a carrier under which capacity is bought: a oneTimeContract (units of a capacity bought once), a permanentContract (a standing number of units, mirrored by virtual vehicles, transport equipment or chauffeurs in a virtual fleet so they can be planned) or a transportOrderContract (the carrier agreement for one forwarded trip). It holds the contract terms, carrier business, administration, period, currency, pricing modules for the carrier cost, a self-billing setting and how bookings are sent (integration, message automation or PDF). Deleting a contract is a soft delete that archives its virtual assets, and creating or updating one triggers afterCreating/afterUpdatingCapacityContract integrations.

### Get a capacity contract

 - [GET /capacityContract/{id}](https://docs.transportial.com/apis/capacitycontract/get:capacitycontract.md): Returns one capacity contract of your platform, including its contract terms, business, pricing
modules, signature and (for one-time and permanent contracts) its capacity.

Permission: get:capacityContract

Errors: 404 when no capacity contract with this id exists on your platform.

### Delete a capacity contract

 - [DELETE /capacityContract/{id}](https://docs.transportial.com/apis/capacitycontract/delete:capacitycontract.md): Marks a capacity contract of your platform as deleted and returns it. The contract is kept in the
database. For a permanent contract the virtual vehicles, transport equipment and chauffeurs it created are
archived (not deleted), because trips, events and invoices may refer to them.

Permission: delete:capacityContract

Errors: 404 when no capacity contract with this id exists on your platform.

### Update a capacity contract

 - [PUT /capacityContract](https://docs.transportial.com/apis/capacitycontract/update:capacitycontract.md): Updates the capacity contract identified by the id in the body; fields left out keep their value. The
type must be sent and cannot be changed. When no contract with that id exists on your platform, a new one is
created with it. Saving re-runs the same side effects as create (business resolution, virtual asset sync for
permanent contracts) and triggers integrations listening to afterUpdatingCapacityContract.

Permission: update:capacityContract

Errors: 400 when the body is not valid JSON or type is missing or unknown; 404 when the id belongs to
a contract of another platform.

### Create a capacity contract

 - [POST /capacityContract](https://docs.transportial.com/apis/capacitycontract/create:capacitycontract.md): Creates a capacity contract: an agreement with a carrier (business) under which you buy its capacity.
The type selects the kind of contract: oneTimeContract (a number of units of a capacity once),
permanentContract (a standing number of units of a capacity) or transportOrderContract (one forwarded trip).

For one-time and permanent contracts, capacity may be an existing capacity (its id as a string, or an object
with an id) or a new capacity object without id, which is created with the contract. A permanent contract
generates the time slots of the capacity's schedule.

On save the business is resolved to an existing business of your platform where possible and, when no
administration is given, the platform's primary administration is used. A saved permanent contract keeps
virtual vehicles, transport equipment or chauffeurs in sync with purchasedUnits (depending on the capacity's
subType), so the bought capacity can be planned. Integrations listening to
afterCreatingCapacityContract are triggered.

Permission: create:capacityContract

Errors: 400 when the body is not valid JSON or type is missing or unknown; 404 when purchasedUnits
is larger than the totalUnits of the capacity (not enough capacity).

### List capacity contracts

 - [GET /capacity/contracts/{offset}/{amount}](https://docs.transportial.com/apis/capacitycontract/get:capacitycontracts.md): Returns one page of the capacity contracts of your platform, most recently updated first. Archived and
deleted contracts are left out. totalResults is the number of contracts across all pages.

Permission: get:capacityContracts

Errors: 400 when offset is not a number.

### Search capacity contracts

 - [POST /capacity/contracts/{offset}/{amount}](https://docs.transportial.com/apis/capacitycontract/search:capacitycontracts.md): Searches the capacity contracts of your platform by free text, contract type, communication method and
start-time range, with optional sorting. Archived and deleted contracts are left out. totalResults is the
number of matches across all pages. An invalid filter value (for example an unparsable date) makes the search
return an empty page.

Permission: get:capacityContracts

Errors: 400 when the body is not valid JSON or offset is not a number.

### Create a transport order contract for a trip

 - [POST /transportOrder/{id}/capacityContract](https://docs.transportial.com/apis/capacitycontract/create:transportorder:capacitycontract.md): Creates a transportOrderContract for a forwarded trip: the agreement with the carrier (business)
that executes the trip. In forwarding, the forwarder's trip represents the transport order, so the id in the
path is the id of a trip. The trip field in the body is ignored and replaced by that trip.

On save the business is resolved to an existing business of your platform where possible, the platform's
primary administration is used when no administration is given, and an e-mail announcing the contract is
sent to the first e-mail contact of the business. Integrations listening to afterCreatingCapacityContract
are triggered.

Permission: create:capacityContract

Errors: 400 when the body is not valid JSON; 404 when no trip with this id exists on your platform.

## ScheduleRoute

A ScheduleRoute is a fixed, recurring line service (for example a ferry, rail or groupage line): a named, ordered list of stop actions with the transport modes it supports, an underlying route, the weekly time slots in which it runs (weekday, start/end time, capacity) and the pricing modules that price it. Time slots, pricing modules and stops are stored together with it. Deleting a schedule route removes it permanently, including its cascaded children.

### Update a schedule route

 - [PUT /scheduleRoute](https://docs.transportial.com/apis/scheduleroute/update:scheduleroute.md): Merges the request body into the stored schedule route identified by the body id; fields left out keep their stored value. When no schedule route with that id exists yet, it is created with that id.

Permission: update:scheduleRoute

Errors: 400 when the body is not valid JSON or does not match the schema; 404 when the id belongs to a schedule route of another platform.

### Create a schedule route

 - [POST /scheduleRoute](https://docs.transportial.com/apis/scheduleroute/create:scheduleroute.md): Creates a schedule route (a recurring line service) with its stops, time slots and pricing modules, which are stored together with it.

Permission: create:scheduleRoute

Errors: 400 when the body is not valid JSON or does not match the schema.

### Get a schedule route

 - [GET /scheduleRoute/{id}](https://docs.transportial.com/apis/scheduleroute/get:scheduleroute.md): Returns one schedule route of your platform with its stops, time slots and pricing modules.

Permission: get:scheduleRoute

Errors: 404 when no schedule route with this id exists on your platform (deleted and archived ones are not found).

### Delete a schedule route

 - [DELETE /scheduleRoute/{id}](https://docs.transportial.com/apis/scheduleroute/delete:scheduleroute.md): Permanently deletes the schedule route; the delete cascades to its stops, time slots and pricing modules. Returns the schedule route as it was before deletion.

Permission: delete:scheduleRoute

Errors: 404 when no schedule route with this id exists on your platform.

### List schedule routes

 - [GET /scheduleRoutes/{offset}/{amount}](https://docs.transportial.com/apis/scheduleroute/get:scheduleroutes.md): Returns one page of the schedule routes of your platform, most recently updated first. Deleted and archived ones are left out.

Permission: get:scheduleRoutes

Errors: 400 when offset is not an integer.

### Search schedule routes

 - [POST /scheduleRoutes/{offset}/{amount}](https://docs.transportial.com/apis/scheduleroute/search:scheduleroutes.md): Full-text search over the schedule routes of your platform, newest first. query is matched against the name, transport modes and stops, as a phrase or, when it contains * or |, with simple query syntax; an empty query returns every schedule route. Deleted and archived ones are left out. amount is used as given (not clamped); invalid paging makes the search return an empty page.

Permission: get:scheduleRoutes

Errors: 400 when the body is missing or not valid JSON, or offset or amount is not an integer.

## Timeslot Booking

Location timeslots are bookable time windows at a location, optionally tied to one loading dock, that recur daily over a date range in the location's time zone. A timeslot booking reserves one occurrence (date) for a delivery or pickup, optionally linked to an action and a consignment, and moves from REQUESTED to CONFIRMED to COMPLETED, or to CANCELLED. REQUESTED and CONFIRMED bookings count towards the timeslot's maximum capacity for that date; availability lists every occurrence with its remaining capacity.

### Get a timeslot booking

 - [GET /timeslot-bookings/{id}](https://docs.transportial.com/apis/timeslot-booking/get:timeslot-booking.md): Returns one timeslot booking of your platform, with references to its timeslot, action, consignment and the user who booked it.

Requires the timeslotBooking feature on the platform.

Permission: get:timeslot-booking

Errors: 404 when no booking with this id exists on your platform (or it is archived or deleted).

### Update a timeslot booking

 - [PUT /timeslot-bookings/{id}](https://docs.transportial.com/apis/timeslot-booking/update:timeslot-booking.md): Changes the notes, the reference number or the timeslot of a REQUESTED or CONFIRMED booking. Moving to another timeslot keeps bookingDate, takes the new occurrence's full window as booked times and runs the same availability and dock checks as booking (see POST /timeslot-bookings); a CONFIRMED booking moves its currentBookings count along.

Requires the timeslotBooking feature on the platform.

Permission: update:timeslot-booking

Errors: 400 when the booking is not REQUESTED or CONFIRMED, or the new timeslot or its dock is not available (the key is in message); 404 when the booking or the new timeslot does not exist on your platform.

### Delete a timeslot booking

 - [DELETE /timeslot-bookings/{id}](https://docs.transportial.com/apis/timeslot-booking/delete:timeslot-booking.md): Soft-deletes a booking. An active (REQUESTED or CONFIRMED) booking is cancelled first with reason Booking deleted, which frees its capacity.

Requires the timeslotBooking feature on the platform.

Permission: delete:timeslot-booking

Errors: 404 when no booking with this id exists on your platform.

### Answer a booking request

 - [POST /timeslot-booking:request/{id}/{responseCode}/respond](https://docs.transportial.com/apis/timeslot-booking/respond:timeslot-booking:request.md): Confirms (CONFIRMED), rejects (FAILED) or proposes other times for a requested slot. A proposal keeps the request REQUESTED until the requester accepts or cancels it.

Permission: none; the code in the path authorizes.

Errors: 400 with the reason's key in message (timeslot_proposal_missing, timeslot_booking_not_respondable) or for an unknown decision; 401 when the answer link is unknown or expired.

### List bookable slots of a booking link

 - [GET /timeslot-booking-link:shared/{id}/{shareCode}/available](https://docs.transportial.com/apis/timeslot-booking/get:timeslot-booking-link:shared:available.md): Returns the bookable occurrences of the link's location between two dates (at most 62 days apart), in the same shape as GET /location-timeslots/available.

Permission: none; the code in the path authorizes.

### Update a booking link

 - [PUT /timeslot-booking-links/{id}](https://docs.transportial.com/apis/timeslot-booking/update:timeslot-booking-link.md): Opens or closes a booking link (isOpen), changes its expiry or its note. The share code stays the same.

Permission: update:timeslot-booking-link; the platform needs the timeslotBooking feature.

### Delete a booking link

 - [DELETE /timeslot-booking-links/{id}](https://docs.transportial.com/apis/timeslot-booking/delete:timeslot-booking-link.md): Soft-deletes a booking link; its URL stops working. Bookings made through it stay.

Permission: delete:timeslot-booking-link; the platform needs the timeslotBooking feature.

### Open a booking link

 - [GET /timeslot-booking-link:shared/{id}/{shareCode}](https://docs.transportial.com/apis/timeslot-booking/get:timeslot-booking-link:shared.md): Returns what the booking page shows: the location, the stop of a per-stop link and the active bookings made through the link.

Permission: none; the code in the path authorizes.

Errors: 401 when the link is unknown, closed or expired, or its owner no longer has the timeslotBooking feature.

### List timeslot bookings

 - [GET /timeslot-bookings](https://docs.transportial.com/apis/timeslot-booking/get:timeslot-bookings.md): Returns the non-deleted, non-archived timeslot bookings of your platform, ordered by booking date and start time, optionally filtered. All filters are combined with AND.

Paginated with the offset and amount query parameters. totalResults is the number of bookings on this page, not the total number of matches.

Requires the timeslotBooking feature on the platform.

Permission: get:timeslot-booking

### Book a timeslot

 - [POST /timeslot-bookings](https://docs.transportial.com/apis/timeslot-booking/create:timeslot-booking.md): Books one occurrence (date) of a location timeslot for a delivery or pickup, optionally linked to an action and a consignment.

The timeslot row is locked while the booking is made. The booking is refused (400, with the key in message) when the timeslot is inactive (timeslot_inactive), bookingDate is outside its date range (timeslot_date_out_of_range) or cancelled by an exception (timeslot_date_cancelled), the cut-off of that date's occurrence has passed (timeslot_booking_cutoff_passed), or the REQUESTED and CONFIRMED bookings for that timeslot on bookingDate already reach maxCapacity (timeslot_full).

bookingStartTime and bookingEndTime default to the occurrence's window and must lie within it (timeslot_booking_outside_occurrence, timeslot_booking_times_invalid). When the timeslot is tied to a loading dock, the dock must be operational and the booked times must keep the number of simultaneous bookings at the dock, across all its timeslots, within maxConcurrentOperations (loading_dock_full).

The new booking gets status REQUESTED, bookedAt now and bookedBy the authenticated user. With autoConfirm: true it is confirmed right away (CONFIRMED, confirmedAt set) and the timeslot's currentBookings counter is increased.

Requires the timeslotBooking feature on the platform.

Permission: create:timeslot-booking

Errors: 400 when the body is not valid JSON, timeslotId or bookingDate is missing, the timeslot or its dock is not available for the booked date and times, or the booking collided with a concurrent change (retry); 404 when the timeslot, action or consignment does not exist on your platform (unknown, deleted or owned by another platform). An unparseable bookingDate currently fails with 500.

### List bookable slots at someone else's dock

 - [GET /timeslot-bookings/outbound/available](https://docs.transportial.com/apis/timeslot-booking/get:timeslot-bookings:outbound:available.md): Reads the bookable occurrences of the location's owner through the booking link they shared (location.externalTimeslotBookingUrl), in the same shape as GET /location-timeslots/available. Pass a timeslotId and date from here to POST /timeslot-bookings/outbound with channel TRANSPORTIAL.

Permission: get:timeslot-booking; the platform needs the timeslotBooking feature.

Errors: 400 with the reason's key in message: timeslot_no_external_link or timeslot_external_link_invalid; 404 when the location does not exist on your platform.

### Complete a timeslot booking

 - [POST /timeslot-bookings/{id}/complete](https://docs.transportial.com/apis/timeslot-booking/complete:timeslot-booking.md): Marks a CONFIRMED booking as done: sets status COMPLETED and records the actual arrival and departure (now when not sent), and decreases the timeslot's currentBookings counter. A completed booking no longer counts towards the capacity of its date.

Requires the timeslotBooking feature on the platform.

Permission: complete:timeslot-booking

Errors: 400 when the booking is not CONFIRMED or a time is not an ISO 8601 instant; 404 when no booking with this id exists on your platform.

### List location timeslots

 - [GET /location-timeslots](https://docs.transportial.com/apis/timeslot-booking/get:location-timeslots.md): Returns the non-deleted, non-archived location timeslots of your platform, ordered by start, optionally filtered by location, loading dock and active flag. All filters are combined with AND.

Paginated with the offset and amount query parameters. totalResults is the number of timeslots on this page, not the total number of matches. Use GET /location-timeslots/available for the bookable occurrences per date.

Requires the timeslotBooking feature on the platform.

Permission: get:location-timeslots

### Create a location timeslot

 - [POST /location-timeslots](https://docs.transportial.com/apis/timeslot-booking/create:location-timeslot.md): Publishes a bookable timeslot at a location, optionally tied to one loading dock.

start and end (UTC instants) set the date range and the daily times: every date from start's date to end's date, in the location's time zone, has one occurrence from start's time of day to end's time of day. The daily start must be before the daily end (no overnight slots). The location and dock are given by id (locationId, loadingDockId) or as associations, and are resolved on your platform only; the dock must belong to the location.

Requires the timeslotBooking feature on the platform.

Permission: create:location-timeslot

Errors: 400 when the location, start or end is missing, a time is not an ISO 8601 instant, start is after end, the daily start is not before the daily end, maxCapacity is negative or the dock belongs to another location; 404 when locationId or loadingDockId does not exist on your platform.

### Get a location timeslot

 - [GET /location-timeslots/{id}](https://docs.transportial.com/apis/timeslot-booking/get:location-timeslot.md): Returns one location timeslot of your platform.

Requires the timeslotBooking feature on the platform.

Permission: get:location-timeslots

Errors: 404 when no timeslot with this id exists on your platform (unknown, deleted or owned by another platform).

### Update a location timeslot

 - [PUT /location-timeslots/{id}](https://docs.transportial.com/apis/timeslot-booking/update:location-timeslot.md): Updates a location timeslot. Only the fields in the body change; send loadingDockId: null to untie the timeslot from its dock. Existing bookings are kept as they are.

start and end (UTC instants) set the date range and the daily times: every date from start's date to end's date, in the location's time zone, has one occurrence from start's time of day to end's time of day. The daily start must be before the daily end (no overnight slots). The location and dock are given by id (locationId, loadingDockId) or as associations, and are resolved on your platform only; the dock must belong to the location.

Requires the timeslotBooking feature on the platform.

Permission: update:location-timeslot

Errors: 400 for the same validation failures as on create; 404 when the timeslot, locationId or loadingDockId does not exist on your platform.

### Delete a location timeslot

 - [DELETE /location-timeslots/{id}](https://docs.transportial.com/apis/timeslot-booking/delete:location-timeslot.md): Soft-deletes a location timeslot. Refused while it has REQUESTED or CONFIRMED bookings: cancel or complete those first.

Requires the timeslotBooking feature on the platform.

Permission: delete:location-timeslot

Errors: 400 when the timeslot still has active bookings; 404 when no timeslot with this id exists on your platform.

### Confirm a timeslot booking

 - [POST /timeslot-bookings/{id}/confirm](https://docs.transportial.com/apis/timeslot-booking/confirm:timeslot-booking.md): Confirms a REQUESTED booking: sets status CONFIRMED and confirmedAt to now, and increases the timeslot's currentBookings counter. Capacity is not checked again: REQUESTED bookings already count towards their date.

Requires the timeslotBooking feature on the platform.

Permission: confirm:timeslot-booking

Errors: 400 when the booking is not REQUESTED; 404 when no booking with this id exists on your platform.

### List booking links

 - [GET /timeslot-booking-links](https://docs.transportial.com/apis/timeslot-booking/get:timeslot-booking-links.md): Returns the booking links of your platform, newest first, optionally for one location or one stop.

Permission: get:timeslot-booking-link; the platform needs the timeslotBooking feature.

### Create a booking link

 - [POST /timeslot-booking-links](https://docs.transportial.com/apis/timeslot-booking/create:timeslot-booking-link.md): Creates a booking link for a location (locationId) or for one stop (actionId; the location defaults to the stop's). Anyone holding the returned url can book the location's timeslots without an account; a per-stop link attaches the booking to the stop. Another Transportial platform books through the same link once it is stored as externalTimeslotBookingUrl on their location.

Permission: create:timeslot-booking-link; the platform needs the timeslotBooking feature.

Errors: 400 when neither locationId nor a stop with a location is given; 404 when the location or stop does not exist on your platform.

### Accept the counterparty's proposed times

 - [POST /timeslot-bookings/{id}/accept-proposal](https://docs.transportial.com/apis/timeslot-booking/confirm:timeslot-booking:proposal.md): Takes the times the counterparty proposed for a MESSAGE request (proposedStartTime/proposedEndTime) as the booked times and confirms the booking.

Permission: confirm:timeslot-booking; the platform needs the timeslotBooking feature.

Errors: 400 with the reason's key in message (timeslot_proposal_missing) or when the booking is not REQUESTED; 404 when no booking with this id exists on your platform.

### Cancel a timeslot booking

 - [POST /timeslot-bookings/{id}/cancel](https://docs.transportial.com/apis/timeslot-booking/cancel:timeslot-booking.md): Cancels a booking: sets status CANCELLED, cancelledAt to now and cancellationReason to the given reason. When the booking was CONFIRMED, the timeslot's currentBookings counter is decreased. A cancelled booking no longer counts towards the capacity of its timeslot date.

Bookings in any status except CANCELLED can be cancelled.

Requires the timeslotBooking feature on the platform.

Permission: cancel:timeslot-booking

Errors: 400 when the body is not a JSON object (send {} without a reason) or the booking is already cancelled; 404 when no booking with this id exists on your platform (or it is archived or deleted).

### Book a slot at someone else's dock

 - [POST /timeslot-bookings/outbound](https://docs.transportial.com/apis/timeslot-booking/create:timeslot-booking:outbound.md): Books a slot at a dock that is not ours (direction OUTBOUND), in one of three ways:

- RECORDED: the counterparty gave us the slot; the booking is CONFIRMED at once.
- MESSAGE: the booking is REQUESTED and sent through the platform's booking-request message automation (planningSettings.timeslotBookingSettings.requestMessageAutomationId) with an answer link; the counterparty confirms, rejects (FAILED) or proposes other times there.
- TRANSPORTIAL: the location's owner shared a booking link from their own Transportial platform (location.externalTimeslotBookingUrl); the slot (timeslotId from GET /timeslot-bookings/outbound/available) is booked on their platform and mirrored here. Status changes on either side follow on the other.

A confirmed booking for a stop sets the stop's planning window to the booked times unless applyBookedWindowToStop is off.

Permission: create:timeslot-booking; the platform needs the timeslotBooking feature.

Errors: 400 with the reason's key in message: timeslot_request_automation_missing, timeslot_no_external_link, timeslot_external_link_invalid, timeslot_booking_times_invalid or one of the availability keys of POST /timeslot-bookings; 404 when a referenced entity does not exist on your platform.

### List bookable timeslots of a location

 - [GET /location-timeslots/available](https://docs.transportial.com/apis/timeslot-booking/get:location-timeslots:available.md): Returns every occurrence of the active timeslots of a location between startDate and endDate (inclusive, days in the location's time zone), with capacity information, for a date-time picker.

- A timeslot produces one occurrence per day of its own date range; exceptions for a date replace its times, title or description, and cancelled dates are left out.
- dockId keeps timeslots of that dock plus timeslots not tied to a dock; slotType keeps timeslots of that type plus BOTH.
- capacity.current counts the REQUESTED and CONFIRMED bookings of that timeslot on that date.

The response is a plain envelope {success, data, total}.

Requires the timeslotBooking feature on the platform.

Permission: get:location-timeslots

Errors: 400 when locationId, startDate or endDate is missing or slotType is not a known value; 404 when the location does not exist on your platform. An unparseable date currently fails with 500.

### Book through a booking link

 - [POST /timeslot-booking-link:shared/{id}/{shareCode}/bookings](https://docs.transportial.com/apis/timeslot-booking/create:timeslot-booking-link:shared:booking.md): Books one occurrence of the link's location. The same checks apply as for a planner booking (date range, cancelled dates, cut-off, capacity per date, booked times inside the occurrence, room at the dock). The booking is CONFIRMED at once unless the owner turned autoConfirmLinkBookings off; a per-stop link attaches it to the stop and replaces the stop's previous link booking.

Permission: none; the code in the path authorizes.

Errors: 400 with the reason's key in message: timeslot_booker_required or one of the availability keys of POST /timeslot-bookings; 401 when the link is unknown, closed or expired; 404 when the timeslot is not one of the link's location.

### Open a booking request

 - [GET /timeslot-booking:request/{id}/{responseCode}](https://docs.transportial.com/apis/timeslot-booking/get:timeslot-booking:request.md): Returns the slot a Transportial platform requested at your dock, for the answer page linked from the request message.

Permission: none; the code in the path authorizes.

### Cancel a booking made through a booking link

 - [POST /timeslot-booking-link:shared/{id}/{shareCode}/bookings/{bookingId}/cancel](https://docs.transportial.com/apis/timeslot-booking/cancel:timeslot-booking-link:shared:booking.md): Cancels an active booking that the link shows (made through it, or a per-stop link's booking for the stop).

Permission: none; the code in the path authorizes.

## Booking

A reservation of capacity or a service with an external party under a capacity contract: sea/ferry, rail, barge, air or road legs, plus energy and service stops. A booking belongs to a transport order and is sent to the carrier through the contract's integration or message automation when the trip with its booking action is planned or confirmed; carriers confirm, reject or cancel it through the carrier webhook, which updates the status and booking number. Create, update, cancel and confirm trigger integrations.

### Update a booking

 - [PUT /booking](https://docs.transportial.com/apis/booking/update:booking.md): Updates the booking identified by the id in the body. Fields present in the body overwrite the stored values; omitted fields are kept. The subtype (type) cannot be changed. When no booking with this id exists yet, a new one is created with it. Saving queues integrations listening for AfterUpdatingBooking (or AfterCreatingBooking for a new booking).

Permission: update:booking

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a booking of another platform.

### Create a booking

 - [POST /booking](https://docs.transportial.com/apis/booking/create:booking.md): Creates a booking of the subtype given by type on your platform and returns it. The capacity contract is resolved to the existing contract. Saving queues integrations listening for AfterCreatingBooking. The booking is not sent to the carrier by this endpoint; bookings are sent when the trip carrying the booking action is planned or confirmed.

Permission: create:booking

Errors: 400 when the body is not valid JSON or type is missing or unknown; 404 when the body carries the id of an existing booking (creating never overwrites an existing booking).

### Get a booking

 - [GET /booking/{id}](https://docs.transportial.com/apis/booking/get:booking.md): Returns a single booking of your platform, serialised as its subtype (type), with its transport order, capacity contract, quote and invoice.

Permission: get:booking

Errors: 404 when no booking with this id exists on your platform.

### Delete a booking

 - [DELETE /booking/{id}](https://docs.transportial.com/apis/booking/delete:booking.md): Soft-deletes a booking: it is flagged as deleted and no longer listed or searchable. Returns the deleted booking. The carrier is not notified; cancel the trip or booking for that.

Permission: delete:booking

Errors: 404 when no booking with this id exists on your platform.

### List bookings

 - [GET /bookings/{offset}/{amount}](https://docs.transportial.com/apis/booking/get:bookings.md): Returns one page of the bookings of your platform (all subtypes), most recently updated first, with the total number of bookings. Deleted and archived bookings are excluded.

Permission: get:bookings

Errors: 400 when offset is not an integer.

### Search bookings

 - [POST /bookings/{offset}/{amount}](https://docs.transportial.com/apis/booking/search:bookings.md): Full-text search over the bookings of your platform. query is matched as a phrase against the booking's name, booking number, external reference and subtype references; an empty body returns all bookings. Results are ordered most recently updated first and paged with offset and amount. A failing search returns an empty page.

Permission: get:bookings

Errors: 400 when the body is not valid JSON or offset is not an integer.

### Receive a carrier booking confirmation

 - [POST /booking/carrier/{carrierId}/confirm](https://docs.transportial.com/apis/booking/confirm:booking:carrier.md): Webhook for ferry and air carriers to confirm, reject or cancel a booking they received.

The carrier is identified by carrierId (a manual integration type such as dfds, stena_line, po_ferries or cldn). Of the enabled integrations of that type, the one whose credentials match the request is used: X-API-KEY (API-key authentication), X-API-KEY plus X-API-SECRET (access-key authentication), HTTP basic authentication or a bearer token, depending on how the integration is configured. The booking is looked up by externalReference on that integration's platform. The carrier's bookingReference becomes the booking number and status sets the booking status (CONFIRMED; FAILED or REJECTED become failed; CANCELLED; anything else pendingConfirmation); then integrations listening for AfterConfirmingBooking are queued. A repeated delivery that changes neither status nor booking number is acknowledged without a write or event.

The response is a data envelope whose data is a JSON string {"bookingId": ..., "bookingNumber": ..., "status": ...}.

Permission: Public, no user authentication (the carrier authenticates with its integration credentials).

Errors: 400 when the carrier is unknown or externalReference is missing; 401 when no enabled integration of this carrier matches the credentials (integrations without configured credentials never match); 404 when no booking with this external reference exists on that platform; 500 on unexpected failures.

## EnergyBooking

Fuelling and charging stops planned as energy bookings (a booking subtype carrying fuel type, quantity in L/kWh/kg, provider, location, planned arrival and estimated cost). Energy-stop planning checks a trip's route distance against its vehicle's fuel capacity, consumption and safety margin from the platform's energy settings and, when auto-planning is enabled and a stop is needed, creates one draft energy booking per trip.

### Plan energy stops for a trip

 - [POST /bookings/energy/plan/{tripId}](https://docs.transportial.com/apis/energybooking/create:booking:energy-stops.md): Checks whether a trip can be driven on one tank or charge and, if not, creates a fuelling or charging stop as a draft energy booking. Uses the platform's energy settings: nothing is created unless auto-planning of energy stops is enabled. A stop is needed when the trip distance times the default consumption per km exceeds the vehicle's fuel capacity minus the safety margin. The booking gets the vehicle's fuel type, the rounded-up shortfall as quantity (in L, kWh or kg), the first preferred provider, booking number ENERGY- and the transport order of the trip's consignments.

Returns the created bookings: an empty list when no stop is needed, auto-planning is disabled, the trip has no vehicle, fuel capacity or route distance, the trip already has a non-cancelled energy booking, or no transport order can be found. The request has no body.

Permission: create:booking

Errors: 404 when no trip with this id exists on your platform.

## Calendar Events

Calendar events block or reserve a resource (vehicle, transport equipment or chauffeur, at most one per event) for a period: leave, maintenance, inspections, shifts. An event is one-time (own start/end date and time) or recurring (daily, weekly, monthly, yearly or a weeks-on/weeks-off rotation) with per-date exceptions that cancel or change single occurrences. Occurrences are calculated on request, never stored, as wall-clock times in the event's location or platform time zone; only blocking events make an asset unavailable in the availability checks. A bulk planner applies one recurrence spec to many assets as calendar events, chauffeur timeslots or to-dos.

### Check whether a vehicle is available

 - [GET /calendar/vehicles/{vehicleId}/available](https://docs.transportial.com/apis/calendar-events/isvehicleavailable.md): Checks whether the vehicle is free for the whole period from startDateTime to endDateTime: available is true when no blocking calendar event occurrence of the vehicle overlaps the period. Non-blocking events, cancelled occurrences and events of other platforms are ignored; an unknown vehicle id is reported as available.

Permission: read:asset_calendar

Errors: a missing or malformed startDateTime/endDateTime is not mapped to 400; it currently results in a server error (500).

### Cancel or change one occurrence of a series

 - [POST /calendar/recurring-events/{recurringEventId}/exception](https://docs.transportial.com/apis/calendar-events/addeventexception.md): Adds an exception for one date of a recurring series, or replaces the existing exception for that date. With isCanceled true (the default) the occurrence on exceptionDate is left out of occurrences and no longer blocks availability; with false it stays, using the replacement start/end time, title and description where given. Every field of an existing exception is overwritten, so omitted replacement values are cleared.

Permission: update:calendar_event

Errors: 404 when no recurring event with this id exists on your platform; 400 for a malformed body.

### List timeslot occurrences of a chauffeur

 - [GET /chauffeurs/{chauffeurId}/timeslot-occurrences](https://docs.transportial.com/apis/calendar-events/getchauffeurtimeslotoccurrences.md): Expands the chauffeur's timeslots (chauffeur activities) overlapping the requested range into daily
occurrences, applies the timeslot exceptions (cancelled or rescheduled dates) and returns the occurrences that
overlap the range, sorted by start time. Open-ended timeslots (no end) are treated as ongoing until the end of the
range. Only timeslots on your platform are included.

Both startDateTime and endDateTime are required. An unknown chauffeur id yields an empty list, not a 404.

Permission: read:asset_calendar

Errors: a missing or malformed startDateTime/endDateTime is not mapped to 400; it results in a server
error.

### Update a calendar event

 - [PUT /calendarEvent](https://docs.transportial.com/apis/calendar-events/update:calendarevent.md): Updates the calendar event identified by id in the body. Scalar fields present in the body overwrite the stored values. The relations location, vehicle, transportEquipment and chauffeur are always replaced: send each one you want to keep (as an id string or an object with an id), because an omitted relation is cleared. A relation id must exist on your platform; the relation already linked to the event is always accepted. An id that does not exist yet creates a new event.

An event may be linked to at most one asset.

Permission: update:calendarEvent

Errors: 404 when the id belongs to an event of another platform, or a relation id is unknown or belongs to another platform; 400 for a malformed body or failed validation. Linking more than one asset currently fails with 500.

### Create a calendar event

 - [POST /calendarEvent](https://docs.transportial.com/apis/calendar-events/create:calendarevent.md): Creates a calendar event, typically a one-time event with startDate/startTime and endDate/endTime. The relations location, vehicle, transportEquipment and chauffeur are accepted as an id string or as an object with an id and must exist on your platform. createdBy is set to the calling user. A nested recurringEvent is saved together with the event; recurring events are more conveniently created with POST /calendar/recurring-events.

An event may be linked to at most one asset (vehicle, transport equipment or chauffeur).

Permission: create:calendarEvent

Errors: 400 for a malformed body; 404 when location, vehicle, transportEquipment or chauffeur is unknown or belongs to another platform. Linking more than one asset is rejected, but currently as a server error (500) rather than 400.

### Get a calendar event

 - [GET /calendarEvent/{id}](https://docs.transportial.com/apis/calendar-events/get:calendarevent.md): Returns one calendar event of your platform, including its recurrence rule when it repeats.

Permission: get:calendarEvent

Errors: 404 when no event with this id exists on your platform, or it is archived or deleted.

### Delete a calendar event

 - [DELETE /calendarEvent/{id}](https://docs.transportial.com/apis/calendar-events/delete:calendarevent.md): Soft-deletes the calendar event: it is marked deleted and no longer appears in lists, searches, occurrences or availability checks. A recurring event is removed with all its occurrences.

Permission: delete:calendarEvent

Errors: 404 when no event with this id exists on your platform, or it is already deleted.

### Apply one recurrence spec to many targets

 - [POST /calendar/recurring-events/bulk](https://docs.transportial.com/apis/calendar-events/bulkcreaterecurringevent.md): The bulk planner. Resolves the targets (explicit ids plus every match of chauffeurFilter and transportEquipmentFilter, up to 10000 per filter; ids that are unknown or belong to another platform are skipped) and creates, depending on targetType:

- CALENDAR_EVENT: one recurring calendar event per chauffeur, transport equipment and vehicle, each with the given rule.
- CHAUFFEUR_TIMESLOT: one recurring chauffeur activity per chauffeur, of activityType (default AVAILABLE), starting at startDate + startTime and ending at endDate + endTime (open-ended without endDate) in the calling user's time zone. Transport equipment and vehicles are ignored.
- TODO: one to-do per target in lane todoLaneId, named  — , with its deadline at endDate (else startDate) + endTime. The recurrence fields are not used.

Use POST /calendar/recurring-events/bulk/preview first to see which targets a request resolves to.

Permission: create:calendar_event

Errors: 400 for a malformed body or an unknown enum value; 404 when location is not a location of your platform, or todoAssigneeId is not a user with access to your platform. No resolvable target, TODO without todoLaneId or with an unknown lane, more than one asset on an event, or an invalid rotation are rejected but currently as a server error (500).

### List calendar occurrences of a vehicle

 - [GET /calendar/vehicles/{vehicleId}/events](https://docs.transportial.com/apis/calendar-events/getvehiclecalendarevents.md): Expands the calendar events linked to the vehicle into concrete occurrences overlapping the period from startDateTime to endDateTime, sorted by start. Recurring events are expanded per date with their exceptions applied; one-time events need start and end date and time to be included. Occurrence times are wall-clock times in the event's zone (its location's, else the platform's) and are compared with the period in that zone. Both blocking and non-blocking events are returned. Only events on your platform are included; an unknown vehicle id yields an empty list, not a 404.

Permission: read:asset_calendar

Errors: a missing or malformed startDateTime/endDateTime is not mapped to 400; it currently results in a server error (500).

### List calendar event occurrences of a chauffeur

 - [GET /chauffeurs/{chauffeurId}/calendar-occurrences](https://docs.transportial.com/apis/calendar-events/getchauffeurcalendarevents.md): Expands the calendar events linked to the chauffeur (as an asset) into concrete occurrences within the
requested range, with recurring-event exceptions applied, sorted by start time. Only events on your platform are
included. Chauffeur timeslots are not part of this result; see GET /chauffeurs/{chauffeurId}/timeslot-occurrences.

Both startDateTime and endDateTime are required. An unknown chauffeur id yields an empty list, not a 404.

Permission: read:asset_calendar

Errors: a missing or malformed startDateTime/endDateTime is not mapped to 400; it results in a server
error.

### List calendar occurrences in a date range

 - [GET /calendar/recurring-events/occurrences](https://docs.transportial.com/apis/calendar-events/geteventoccurrences.md): Expands all calendar events of your platform into concrete occurrences between startDate and endDate (inclusive), sorted by start. Recurring events are expanded per date with their exceptions applied (cancelled dates are left out, modified dates carry the replacement values). One-time events are included when their date span overlaps the range and they have start and end date and time; they appear once, dated on their start date. Deleted events are left out. Pass userId to include only events created by that user.

Permission: read:calendar_event

Errors: a missing or malformed startDate/endDate is not mapped to 400; it currently results in a server error (500).

### List calendar occurrences of transport equipment

 - [GET /calendar/transport-equipment/{equipmentId}/events](https://docs.transportial.com/apis/calendar-events/gettransportequipmentcalendarevents.md): Expands the calendar events linked to the transport equipment into concrete occurrences overlapping the period from startDateTime to endDateTime, sorted by start. Recurring events are expanded per date with their exceptions applied; one-time events need start and end date and time to be included. Occurrence times are wall-clock times in the event's zone (its location's, else the platform's) and are compared with the period in that zone. Both blocking and non-blocking events are returned. Only events on your platform are included; an unknown transport equipment id yields an empty list, not a 404.

Permission: read:asset_calendar

Errors: a missing or malformed startDateTime/endDateTime is not mapped to 400; it currently results in a server error (500).

### Search calendar events

 - [POST /calendarEvents/search/{offset}/{amount}](https://docs.transportial.com/apis/calendar-events/search:calendarevents.md): Searches the calendar events of your platform by text (title or description, case-insensitive substring), asset (vehicle, transport equipment or chauffeur id) and date range, sorted by start date and start time, latest first. Archived and deleted events are left out.

The date filters compare the events' own startDate/endDate, so recurring events (which have none) drop out when a date filter is used. To find what happens in a period, use the occurrence endpoints instead.

Permission: search:calendarEvents

Errors: 400 for a malformed body or a non-integer offset. An unparsable date, or any other failure while searching, returns an empty page with totalResults 0.

### Check whether transport equipment is available

 - [GET /calendar/transport-equipment/{equipmentId}/available](https://docs.transportial.com/apis/calendar-events/istransportequipmentavailable.md): Checks whether the transport equipment is free for the whole period from startDateTime to endDateTime: available is true when no blocking calendar event occurrence of the transport equipment overlaps the period. Non-blocking events, cancelled occurrences and events of other platforms are ignored; an unknown transport equipment id is reported as available.

Permission: read:asset_calendar

Errors: a missing or malformed startDateTime/endDateTime is not mapped to 400; it currently results in a server error (500).

### List calendar events

 - [GET /calendarEvents/{offset}/{amount}](https://docs.transportial.com/apis/calendar-events/get:calendarevents.md): Returns a page of the calendar events of your platform (one-time and recurring), sorted by start date and start time, latest first. Archived and deleted events are left out. Recurring events are returned once, as the event with its rule; use the occurrence endpoints to expand them.

Permission: get:calendarEvents

Errors: 400 when offset is not an integer. A query that fails internally returns an empty page.

### Check whether a chauffeur is available

 - [GET /chauffeurs/{chauffeurId}/available](https://docs.transportial.com/apis/calendar-events/ischauffeuravailable.md): Checks the chauffeur's calendar for the period from startDateTime to endDateTime.

The chauffeur is reported as available (available: true) only when at least one occurrence of a recurring calendar event of the chauffeur falls in the period and no chauffeur activity (timeslot) of the chauffeur overlaps it (an activity without end counts as ongoing). In every other case available is false, including when the chauffeur has no calendar events at all in the period.

Permission: read:asset_calendar

Errors: an unknown chauffeur id, or a missing or unparsable date, currently fails with 500 instead of 404/400.

### Preview the targets of a bulk-planner run

 - [POST /calendar/recurring-events/bulk/preview](https://docs.transportial.com/apis/calendar-events/bulkplannerpreview.md): Resolves explicit ids and search filters into the chauffeurs, transport equipment and vehicles of your platform a bulk-planner run would apply to, without creating anything; ids that are unknown or belong to another platform are skipped. Each filter contributes at most limit matches (capped at 10000) and each list returns at most limit previews; the totals count all resolved targets. For CHAUFFEUR_TIMESLOT only chauffeurs are resolved.

Permission: create:calendar_event

Errors: 400 for a malformed body or an unknown targetType.

### Create a recurring calendar event

 - [POST /calendar/recurring-events](https://docs.transportial.com/apis/calendar-events/createrecurringevent.md): Creates a calendar event with a recurrence rule in one request: the base event (title, description, location, asset, blocking) and the rule (pattern, interval, series window, daily time slot, weekdays or rotation). createdBy is set to the calling user. Occurrences are not stored; they are calculated by the occurrence and availability endpoints.

Link at most one asset (vehicleId, transportEquipmentId or chauffeurId); the asset and location ids must exist on your platform. Always send startTime and endTime: they default to the server's current time.

Permission: create:calendar_event

Errors: 400 for a malformed body or an unknown enum value; 404 when the location or asset id is unknown or belongs to another platform. Linking more than one asset, or a rotation pattern without positive weeksOn and weeksOff, is rejected but currently as a server error (500).

## Tender

A tender puts one consignment out to carriers on the marketplace, with a target price; carriers respond with offers (bids) that can be accepted, declined or countered. Accepting an offer marks it accepted, marks the other open offers not_chosen, closes the tender and withdraws its load-board postings; accept-and-plan also creates a trip for the consignment. Tenders created on the platform (source INTERNAL) are published to the platform's freight-exchange integrations through the afterCreatingTender/afterUpdatingTender/afterCancelingTender events; tenders imported from an exchange or received through the inbound API keep their external id so the same posting is never stored twice. A consignment can carry only one tender.

### List tenders

 - [GET /tenders/{offset}/{amount}](https://docs.transportial.com/apis/tender/get:tenders.md): Returns a page of the tenders of your platform, most recently updated first. Archived and deleted tenders are left out. Each tender includes its consignment.

Use POST /tenders/{offset}/{amount} to filter.

Permission: get:tenders

### Search tenders

 - [POST /tenders/{offset}/{amount}](https://docs.transportial.com/apis/tender/search:tenders.md): Searches the tenders of your platform with free text and filters (status, source, type, business, offer count, auto-accept, creation date, stop windows, radius around a location and route corridor). All filters are combined with AND; archived and deleted tenders are never returned. Results are sorted by sortBy, or by relevance when it is empty. Each tender includes its consignment.

The corridor filter is applied after the search: all matches are loaded, filtered on their stops and then paged, so totalResults is the filtered count.

Permission: search:tenders

Errors: a search that fails internally (for example an invalid sortBy field) returns an empty page with totalResults 0 instead of an error.

### Accept an offer

 - [GET /tender/bid/{id}/accept](https://docs.transportial.com/apis/tender/accept:tenderbid.md): Accepts the offer (bid): it becomes accepted, every other offer on the same tender that is still offered or concept becomes not_chosen, and the tender is set to closed with this offer as its accepted offer. The afterCancelingTender integration event is queued so load boards withdraw the tender's postings. No trip is created; use POST /tender/{id}/accept-and-plan to also plan the work.

Note that this state-changing operation uses GET.

Permission: create:tender

Errors: 404 when the offer does not exist on your platform.

### Decline an offer

 - [POST /tender/bid/{id}/decline](https://docs.transportial.com/apis/tender/decline:tenderbid.md): Declines the offer (bid): its status becomes declined and its remark is replaced by the given reasoning (empty when none). The tender itself is not changed and stays open for other offers.

Permission: decline:tender:bid

Errors: 404 when the offer does not exist on your platform; 400 for a malformed body.

### Counter an offer

 - [POST /tender/bid/{id}/counter](https://docs.transportial.com/apis/tender/counter:tender.md): Answers an offer (bid) with a counter offer. The body is the new offer; it is created on the same tender with status offered and business set to your platform's own business. The original offer is declined (its remark cleared) and points to the new one through counterBid.

The tender's aggregates are updated as for a new offer: totalBidCount is incremented, the counter offer becomes mostRecentBid, and lowestAmountBid when its amount is lower.

Permission: counter:tender:bid

Errors: 404 when the offer or its tender does not exist on your platform, or your platform has no business; 400 for a malformed body.

### Update a tender

 - [PUT /tender](https://docs.transportial.com/apis/tender/update:tender.md): Updates the tender identified by id in the body. Fields present in the body overwrite the stored values; read-only fields (externalAttributes, offer aggregates) are ignored, and business is reset to your platform's own business. An id that does not exist yet creates a new tender.

After saving, an integration event is queued for the enabled integrations that listen to it: afterCancelingTender when the status moves into accepted or closed (load boards withdraw the posting), otherwise afterUpdatingTender (load boards update the posting).

Permission: update:tender

Errors: 404 when the id belongs to a tender of another platform, or your platform has no business; 400 for a malformed body or failed validation.

### Create a tender

 - [POST /tender](https://docs.transportial.com/apis/tender/create:tender.md): Puts a consignment out to tender. The body is a tender with at least consignment (a reference to an existing consignment); business is always set to your platform's own business and the creator to the calling user. Without a status the tender is created open, so it accepts offers right away.

After saving, the afterCreatingTender event is queued for the enabled integrations that listen to it; load-board integrations use it to publish internal tenders to their freight exchange.

A consignment can carry only one tender: when it already has one, nothing is created and the response is HTTP 200 with success: false and an explanatory message.

Permission: create:tender

Errors: 404 when your platform has no business, or the referenced consignment does not exist on your platform; 400 for a malformed body.

### Get a tender

 - [GET /tender/{id}](https://docs.transportial.com/apis/tender/get:tender.md): Returns one tender of your platform, including its consignment, target price and the latest and lowest offers.

Permission: get:tender

Errors: 404 when no tender with this id exists on your platform, or it is archived or deleted.

### Accept an offer and create a trip for it

 - [POST /tender/{id}/accept-and-plan](https://docs.transportial.com/apis/tender/accept:tenderandplan.md): Accepts one offer on the tender and plans the work in one request:

1. The offer becomes accepted, every other offer on the tender that is still offered or concept becomes not_chosen, and the tender is set to closed with this offer as its accepted offer. The afterCancelingTender integration event is queued so load boards withdraw the tender's postings.
2. A trip is created, named after the tender, with startDate from the request and the tender's consignment as its only consignment.

The response holds the tender (without its consignment), the accepted offer and the created trip.

Permission: create:tender

Errors: 404 when the tender does not exist on your platform, the offer does not exist, or the offer belongs to another tender; 400 for a malformed body.

### Place an offer on a tender

 - [POST /tender/{id}/bid](https://docs.transportial.com/apis/tender/create:tenderbid.md): Creates an offer (bid) on the tender in the path. business is set to your platform's own business; the offer is stored as offered, or as a concept draft only when status: "concept" is sent.

The tender's aggregates are updated: totalBidCount is incremented, the offer becomes mostRecentBid, and it becomes lowestAmountBid when its amount is lower than the current lowest offer.

Permission: create:tender:bid

Errors: 404 when the tender does not exist on your platform or your platform has no business; 400 for a malformed body.

### Delete an offer

 - [DELETE /tender/bid/{id}](https://docs.transportial.com/apis/tender/delete:tender.md): Deletes the offer (bid). Before deleting, the offer is detached from its tender: it is cleared as the tender's mostRecentBid, lowestAmountBid or accepted offer where it held that role (they are not recalculated), and totalBidCount is decremented (never below 0).

Despite its operation id, this endpoint deletes an offer, not a tender.

Permission: delete:tenderBid

Errors: 404 when the offer does not exist on your platform, or the request has no user session.

### List the offers on a tender

 - [GET /tender/{id}/bids/{offset}/{amount}](https://docs.transportial.com/apis/tender/get:tenderbids.md): Returns a page of the offers (bids) placed on the tender that belong to your platform, most recently updated first.

A tender id that is unknown, deleted or owned by another platform returns an empty page rather than 404.

Permission: get:tenderBids

Errors: 400 when offset or amount is not an integer.

### Search offers

 - [POST /tender/{id}/bids/{offset}/{amount}](https://docs.transportial.com/apis/tender/search:tenderbids.md): Searches offers (bids) with free text (query), most recently updated first. Archived and deleted offers are left out.

The tender in the path must exist on your platform, but the search itself is not limited to that tender: it covers all offers of your platform. Only query is applied; the other body fields are ignored.

Permission: search:tenders

Errors: 404 when the tender does not exist on your platform; 400 for a malformed body or a non-integer offset. A search that fails internally returns an empty page.

### Receive a tender from an external system

 - [POST /api/v1/tender/inbound](https://docs.transportial.com/apis/tender/create:tender:inbound.md): Entry point for external systems (typically authenticated with an API-access integration's x-api-key and x-api-secret) that hand transport work to the platform as a tender.

The body is a tender. When it carries a non-blank externalId and a tender with that externalId already exists on your platform, nothing is created and the existing tender is returned, so the call can be retried safely. Otherwise the tender is stored as sent, with business set to your platform's own business. Unlike POST /tender, no consignment is required, the one-tender-per-consignment check is not applied and no integration event is queued (the tender is not published to load boards). The server does not set source: send API_INBOUND to mark the tender as received through this API (default INTERNAL).

Permission: create:tender

Errors: 404 when your platform has no business; 400 for a malformed body.

## Marketplace

Live search across the freight exchanges a platform has enabled. The lane, pickup window, equipment and size criteria are pushed into each board's own query; criteria a board cannot express are applied to its answer and reported per board, and a failing board is reported instead of failing the search. Answers are merged, deduplicated and ranked by price per kilometre and are never stored: a posting only becomes a Tender (status open) when it is imported, which re-reads it from the board and is idempotent per posting.

### Search the enabled freight exchanges

 - [POST /marketplace/search/{offset}/{amount}](https://docs.transportial.com/apis/marketplace/search:marketplace.md): Runs a live search on every freight exchange your platform has enabled (or only the sources asked for) and returns what they answer right now. Nothing is stored: use POST /marketplace/import to keep an offer as a tender.

The lane, pickup window, equipment, weight, length, price and age criteria are pushed into each board's own query. Criteria a board cannot express are applied to its answer here and listed in sources[].locallyFiltered; such a filter only sees the page that board chose to return. A board that fails is reported in sources with ok: false and its error instead of failing the whole search.

The answers are merged, deduplicated (the same lane, pickup time and price from two boards appears once) and sorted by price per kilometre, then price, highest first. Each board is asked for offset + amount offers; offset and amount are then applied to the merged list, and totalResults is the number of merged offers.

Permission: search:tenders

Errors: 400 for a malformed body.

### Store a board posting as an open tender

 - [POST /marketplace/import](https://docs.transportial.com/apis/marketplace/import:marketplaceoffer.md): Turns one offer from a marketplace search into a tender. The posting is not taken from the request: the server replays the given criteria against the board of integrationId (up to 100 results), picks the posting with externalId out of the answer, and stores what the board says as an open tender with its lane, time windows, price and load.

Importing the same posting twice is a no-op: the tender that already holds it is returned with alreadyExisted: true. Imported tenders keep the board as their source and are never published back to the platform's own load boards (no integration event is queued).

Permission: create:tender

Errors: 400 when integrationId or externalId is blank, or the body is malformed; 404 when the integration is not a freight-exchange integration of your platform, has no search provider, the posting is no longer in the board's answer, or the tender could not be created.

## Vehicle

A vehicle is a motorised asset of your fleet (truck, van, tractor, vessel) with its dimensions, capacities, emission data, sensors and board computers. Chauffeurs and transport equipment are attached and detached through attach/detach actions, which run immediately or later as planned actions; the vehicle's trips, events, latest position and driven track hang off it. Telemetry endpoints derive FMS views from its sensors: latest values, sensor time series, tachograph driving times, utilization/cost and driving-style scores. Vehicles are soft-deleted; vehicles managed by a capacity contract are virtual and are excluded from the own-fleet FMS views.

### List the events of a vehicle by lifecycle

 - [GET /vehicles/{uuid}/{lifeCycle}/events/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicles:events.md): Intended to list the events of a vehicle in one lifecycle, newest first.

Note: the controller reads the vehicle from a path parameter named id, which this path does not have (it is called uuid), so this endpoint currently fails. Use GET /vehicle/{id}/events/{offset}/{amount} or POST /vehicle/{id}/events/{offset}/{amount} instead.

Permission: Any authenticated user (no specific permission).

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Create an event for a vehicle

 - [POST /vehicle/{uuid}/{lifeCycle}/event](https://docs.transportial.com/apis/vehicle/create:vehicle:event.md): Stores an event of any type (discriminated by its type) on your platform for the vehicle in the path. The event is saved as sent: the lifeCycle path segment is not applied, and the event is only linked to the vehicle when the body references it in vehicle.

Permission: create:vehicle

Errors: 400 when the body is not valid JSON or has an unknown event type; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### List the events of a vehicle

 - [GET /vehicle/{id}/events/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicle:events.md): Lists the non-deleted, non-archived events of a vehicle (position updates, engine and movement events, sensor updates, ...), newest first.

Paginated with offset/amount; totalResults holds the total number of matches. On a query error an empty page is returned.

Permission: Any authenticated user (no specific permission).

Errors: 400 when offset or amount is not an integer; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Search the events of a vehicle

 - [POST /vehicle/{id}/events/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/search:vehicle:events.md): Searches the non-deleted, non-archived events of a vehicle with server-side filters on event type, lifecycle and creation date, and sorting on createdAt, dateTime, type or lifeCycle (default createdAt descending). Filters apply to the whole event set, not only a loaded page.

Paginated with offset/amount; totalResults holds the total number of matches. On a query error an empty page is returned.

Permission: Any authenticated user (no specific permission).

Errors: 400 when the body is not valid JSON or offset/amount is not an integer; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### List vehicle planboard rows

 - [GET /vehicles/board/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicles:board.md): Returns one page of the vehicle planboard as lean row projections instead of full extended vehicles. The set and order of vehicles is the same as GET /vehicles/extended/{offset}/{amount}; only the columns are read with a projection.

The rows are returned under extendedVehicles for compatibility with the extended list. Paginated with offset/amount; totalResults holds the total number of matches.

Permission: get:vehicles:extended

Errors: 400 when offset or amount is not an integer.

### Search vehicle planboard rows

 - [POST /vehicles/board/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/post:vehicles:board.md): Searches the vehicles of your platform like POST /vehicles/extended/{offset}/{amount} and returns the page as lean planboard row projections. Only vehicle ids are taken from the search index; the columns are read with a projection.

Uses the vehicle search index: deleted and archived vehicles are never returned and external (customer) users only see the vehicles of their businesses. If the search itself fails, an empty page is returned instead of an error. Paginated with offset/amount; totalResults holds the total number of matches. The body is stored as the caller's last search for this window, which scopes live updates.

Permission: search:vehicles:extended

Errors: 400 when the body is not valid JSON or offset/amount is not an integer.

### Record that the vehicle stopped moving

 - [POST /vehicle/{uuid}/{lifeCycle}/stopMoving](https://docs.transportial.com/apis/vehicle/create:vehicle:event:stopmoving.md): Creates a stop-moving event for the vehicle at the given geo reference, in the lifecycle from the path (actual when the value is not a known lifecycle), and stores it on the vehicle's platform.

The body's geoReference is either a full geo reference object or the id of an existing geo reference.

Permission: create:vehicle

Errors: 400 when the body is not valid JSON or has no geoReference; 404 when the vehicle does not exist on your platform or the referenced geo reference id is unknown.

### List vehicles with their relations

 - [GET /vehicles/extended/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicles:extended.md): Lists the non-deleted, non-archived vehicles of your platform in their extended form (with current trip, chauffeurs, transport equipment, ...), most recently updated first. External (customer) users only see the vehicles of their businesses.

Paginated with offset/amount; totalResults holds the total number of matches. The request body (usually empty) is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicles:extended

Errors: 400 when offset or amount is not an integer.

### Search vehicles with their relations

 - [POST /vehicles/extended/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/search:vehicles:extended.md): Searches the vehicles of your platform and returns them in their extended form. The free-text query also matches the vehicle's current trip.

Uses the vehicle search index: deleted and archived vehicles are never returned and external (customer) users only see the vehicles of their businesses. If the search itself fails, an empty page is returned instead of an error. Paginated with offset/amount; totalResults holds the total number of matches. The body is stored as the caller's last search for this window, which scopes live updates.

Permission: search:vehicles:extended

Errors: 400 when the body is not valid JSON or offset/amount is not an integer.

### List the trips of a vehicle

 - [GET /vehicle/{id}/trips/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicle:trips.md): Lists all trips of a vehicle, oldest created first. A trip belongs to the vehicle when the vehicle is assigned to it, or when it carries a chauffeur or transport equipment currently attached to the vehicle. External (customer) users only see trips of their businesses.

Paginated with offset/amount; totalResults holds the total number of matches. The request body (usually empty) is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicle:trips

Errors: 400 when offset or amount is not an integer; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Update a vehicle

 - [PUT /vehicle](https://docs.transportial.com/apis/vehicle/update:vehicle.md): Updates the vehicle identified by the body's id by merging the sent fields onto the stored vehicle. A profilePicture may be sent as a reference to an uploaded file.

Saving may trigger integrations, message automations and live updates for the vehicle.

Permission: update:vehicle

Errors: 400 when the body is not valid JSON or a field has the wrong type; 404 when no vehicle with the body's id exists on your platform.

### Create a vehicle

 - [POST /vehicle](https://docs.transportial.com/apis/vehicle/create:vehicle.md): Creates a vehicle (truck, van, tractor, vessel, ...) on your platform. The body is read as a vehicle model; a profilePicture may be sent as a reference to an uploaded file.

Saving may trigger integrations and message automations listening for new vehicles and counts towards the platform's billable assets.

Permission: create:vehicle

Errors: 400 when the body is not valid JSON or a field has the wrong type.

### Get a vehicle with its relations

 - [GET /vehicle/extended/{id}](https://docs.transportial.com/apis/vehicle/get:vehicle:extended.md): Returns one vehicle in its extended form: the vehicle together with its attached relations (current trip, chauffeurs, transport equipment, sensors, board computers, ...).

Permission: get:vehicle

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Get a vehicle

 - [GET /vehicle/{id}](https://docs.transportial.com/apis/vehicle/get:vehicle.md): Returns one vehicle of your platform with its base fields.

Permission: get:vehicle

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Delete a vehicle

 - [DELETE /vehicle/{id}](https://docs.transportial.com/apis/vehicle/delete:vehicle.md): Soft-deletes a vehicle: it is flagged as deleted, disappears from lists and searches, and its billing usage slot is released for reuse. The deleted vehicle is returned.

Vehicles managed by a capacity contract cannot be deleted here; end or edit the contract instead.

Permission: delete:vehicle

Errors: 400 when the vehicle is managed by a capacity contract; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Get the driving-style score of a vehicle

 - [GET /vehicle/{id}/driving-style/{fromDate}/{toDate}](https://docs.transportial.com/apis/vehicle/get:vehicle:drivingstyle.md): Computes the driving-style score of a vehicle between two instants from its telemetry, using the platform's driving-style settings.

Permission: get:vehicle

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform. A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### Get planner data of several vehicles in a range

 - [POST /vehicles/trips-and-events:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/vehicle/get:vehicle:trips-and-events:bydate.md): Batch endpoint for the vehicle planner. For each vehicle id in the body it returns, in one round-trip:

- the trips overlapping the range, as lightweight summaries (same rules as POST /vehicles/trips:byDate/{fromDate}/{toDate});
- the calendar event occurrences planned on the vehicle (recurring events expanded);
- the non-deleted to-dos linked to the vehicle whose deadline or creation time falls inside the range.

Not paginated. The request body is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicle:trips

Errors: 400 when the body is not valid JSON or has no vehicles array. A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### Apply a bulk action to vehicles

 - [POST /vehicles/bulk](https://docs.transportial.com/apis/vehicle/bulk:vehicle.md): Applies one action to several vehicles at once:

- delete: soft-deletes each vehicle and releases its billing usage slot. Vehicles managed by a capacity contract are skipped.
- operational: sets the operational flag of each vehicle to value (default true).

Vehicles that do not exist on your platform are skipped. The response lists the ids that were actually changed.

Permission: delete:vehicle for delete, update:vehicle for operational

Errors: 400 when the body is not valid JSON, ids is empty, or action is unknown.

### Mark a vehicle as empty

 - [POST /vehicle/{id}/capacity/empty](https://docs.transportial.com/apis/vehicle/update:vehicle:capacity:empty.md): Records that the vehicle and its own trailers are empty from emptyFrom: load planned before that moment no longer carries over into later trips. Then recalculates the capacity of the vehicle's open trips (not cancelled, completed or pending completion) starting from that moment, oldest first. Trips the platform only calculates once confirmed are skipped until confirmed.

Permission: update:vehicle

Errors: 400 for an unreadable emptyFrom; 401 without the permission; 404 when no vehicle with this id exists on your platform.

### List the chauffeurs attached to a vehicle

 - [GET /vehicle/{id}/chauffeurs](https://docs.transportial.com/apis/vehicle/get:vehicle:chauffeurs.md): Returns the chauffeurs currently attached to a vehicle. Not paginated.

Permission: Any authenticated user (no specific permission).

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Detach a chauffeur from a vehicle

 - [POST /vehicle/{id}/detach/chauffeur](https://docs.transportial.com/apis/vehicle/decouple:chauffeur.md): Records a detach-chauffeur action on the vehicle. The body is a detach-chauffeur action whose chauffeur references the driver; vehicle is always set to the vehicle in the path.

Without lifeCycle (or with actual) the chauffeur is detached immediately. With lifeCycle: planned and a startTime, the action is stored as planned and executed by the planned-action job once its start time is reached.

Permission: decouple:chauffeur

Errors: 400 when the body's type is not detachChauffeur or a planned action has no startTime; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Get the trips, to-dos and events of a vehicle

 - [GET /vehicle/{id}/trips-todos-and-events:byDateTime/{startDateTime}/{endDateTime}](https://docs.transportial.com/apis/vehicle/get:vehicle:trips-todos-and-events:bydatetime.md): Returns, for one vehicle and range:

- the non-deleted, non-archived trips starting inside the range (matched on the vehicle and its attached chauffeurs and transport equipment);
- the calendar event occurrences planned on the vehicle (recurring events expanded);
- the non-deleted to-dos linked to the vehicle whose deadline or creation time falls inside the range.

Not paginated.

Permission: get:vehicle:trips or get:vehicle:events

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform. A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### Get the trips of several vehicles in a range

 - [POST /vehicles/trips:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/vehicle/get:vehicles:trips:bydate.md): For each vehicle id in the body, returns its non-deleted, non-archived trips that overlap the range (start before toDate and no end or end after fromDate) as lightweight trip summaries, ordered by start date. External (customer) users only see trips of their businesses. Used by the vehicle planner.

Not paginated. The request body is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicle:trips

Errors: 400 when the body is not valid JSON or has no vehicles array. A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### Report the position of a vehicle

 - [POST /vehicle/{uuid}/{lifeCycle}/locationUpdate](https://docs.transportial.com/apis/vehicle/create:vehicle:event:locationupdate.md): Records a position report for the vehicle: a location update event is stored, the vehicle's latest location is updated and the vehicle is saved. Depending on server configuration, the vehicle's current trip is progressed from the position (distance and time to the next stop, ETA/ETD recalculation) and FMS geofence enter/exit alerts are evaluated; live updates are pushed to open views.

The body's geoReference must be a lat/lon point (type: latLonPointGeoReference), sent as an object or as the id of an existing point. The lifeCycle path segment is not used.

Permission: create:vehicle

Errors: 400 when the body is not valid JSON or has no geoReference; 404 when the vehicle does not exist on your platform or the referenced geo reference id is unknown. A geo reference that is not a lat/lon point fails with a server error.

### List the trips of a vehicle by date

 - [GET /vehicle/{id}/trips:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/vehicle/get:vehicle:trips:bydate.md): Lists the non-deleted, non-archived trips of a vehicle whose start date lies between two days (from midnight of fromDate to midnight of toDate, server time zone), ordered by start date. Trips are matched on the vehicle and on its attached chauffeurs.

Not paginated; totalResults is the number of trips returned. The request body (usually empty) is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicle:trips

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform. A date that is not yyyy-MM-dd is not validated and fails with 500.

### List fleet driving-style scores

 - [GET /vehicles/driving-style/{fromDate}/{toDate}/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicles:drivingstyle.md): Returns the driving-style score of each vehicle of one page of the own fleet between two instants, ranked best to worst within the page. Vehicles without samples in the range are kept (sample count 0) and sorted last.

The page is taken from the own fleet of your platform: contract-managed (capacity) vehicles are always excluded. The GET variant pages the whole own fleet; the POST variant accepts an optional body with a free-text query or a vehicles id allow-list (the allow-list wins). amount is clamped to 1..100.

Permission: get:vehicle

Errors: A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### Search fleet driving-style scores

 - [POST /vehicles/driving-style/{fromDate}/{toDate}/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/search:vehicles:drivingstyle.md): Returns the driving-style score of each vehicle of one page of the own fleet between two instants, ranked best to worst within the page. Vehicles without samples in the range are kept (sample count 0) and sorted last.

The page is taken from the own fleet of your platform: contract-managed (capacity) vehicles are always excluded. The GET variant pages the whole own fleet; the POST variant accepts an optional body with a free-text query or a vehicles id allow-list (the allow-list wins). amount is clamped to 1..100.

Permission: get:vehicle

Errors: A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### List the active trips of a vehicle

 - [GET /vehicle/{id}/trips:active/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicle:trips:active.md): Lists the trips of a vehicle that are neither completed nor cancelled (drafts, requested, planned and in-transit trips), ordered by start date. Trips are matched on the vehicle and on its attached chauffeurs and transport equipment. External (customer) users only see trips of their businesses.

Paginated with offset/amount; totalResults holds the total number of matches. The request body (usually empty) is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicle:trips

Errors: 400 when offset or amount is not an integer; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### List the equipment attached to a vehicle

 - [GET /vehicle/{id}/transportEquipments](https://docs.transportial.com/apis/vehicle/get:vehicle:transportequipments.md): Returns the transport equipment (trailers, containers, ...) currently attached to a vehicle. Not paginated.

Permission: Any authenticated user (no specific permission).

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### List fleet utilization and cost per vehicle

 - [GET /vehicles/utilization/{fromDate}/{toDate}/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicles:utilization.md): Returns, per vehicle of one page of the own fleet, the distance driven, engine hours, utilization percentage, fuel used and the resulting costs between two instants. Figures are deltas of the odoMeter, engineTime and totalFuelUsed counter sensors; costs use the platform's FMS fuel price per litre (default 1.80) plus its optional other-cost-per-km rate.

The page is taken from the own fleet of your platform: contract-managed (capacity) vehicles are always excluded. The GET variant pages the whole own fleet; the POST variant accepts an optional body with a free-text query or a vehicles id allow-list (the allow-list wins). amount is clamped to 1..100.

Permission: get:vehicle

Errors: A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### Search fleet utilization and cost per vehicle

 - [POST /vehicles/utilization/{fromDate}/{toDate}/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/search:vehicles:utilization.md): Returns, per vehicle of one page of the own fleet, the distance driven, engine hours, utilization percentage, fuel used and the resulting costs between two instants. Figures are deltas of the odoMeter, engineTime and totalFuelUsed counter sensors; costs use the platform's FMS fuel price per litre (default 1.80) plus its optional other-cost-per-km rate.

The page is taken from the own fleet of your platform: contract-managed (capacity) vehicles are always excluded. The GET variant pages the whole own fleet; the POST variant accepts an optional body with a free-text query or a vehicles id allow-list (the allow-list wins). amount is clamped to 1..100.

Permission: get:vehicle

Errors: A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### Get the tachograph driving times of a vehicle

 - [GET /vehicle/{id}/tacho-times](https://docs.transportial.com/apis/vehicle/get:vehicle:tachotimes.md): Computes the current driving and remaining driving times (continuous, daily, weekly, two-weekly) per tachograph driver slot of the vehicle from its tacho work-state telemetry. Limits come from the platform's FMS settings (EU 561/2006 defaults) and days/weeks are cut in the tacho time zone (default: the platform's region time zone).

A driver slot is only included when the vehicle has its work-state sensor. Read-only.

Permission: get:vehicle

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### List vehicles

 - [GET /vehicles/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicles.md): Lists the non-deleted, non-archived vehicles of your platform, most recently updated first. External (customer) users only see the vehicles of their businesses.

Paginated with offset/amount; totalResults holds the total number of matches. The request body (usually empty) is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicles

Errors: 400 when offset or amount is not an integer.

### Search vehicles

 - [POST /vehicles/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/search:vehicles.md): Searches the vehicles of your platform with free text and filters (availability window, location radius, current trip plan/route status, capacity contract, sorting).

Uses the vehicle search index: deleted and archived vehicles are never returned and external (customer) users only see the vehicles of their businesses. If the search itself fails, an empty page is returned instead of an error. Paginated with offset/amount; totalResults holds the total number of matches. The body is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicles

Errors: 400 when the body is not valid JSON or offset/amount is not an integer.

### Get the load a vehicle carries into its next trip

 - [GET /vehicle/{id}/capacity](https://docs.transportial.com/apis/vehicle/get:vehicle:capacity.md): Returns the load the capacity calculation has on board the vehicle at a moment (default now): what a trip starting then inherits, the capacity left, the empty capacity and the capacity event it comes from (with its trip). Events of cancelled trips never carry over. A vehicle without earlier capacity events is empty.

Permission: get:vehicle

Errors: 401 without the permission; 404 when no vehicle with this id exists on your platform.

### Get the next open trips of several vehicles

 - [POST /vehicles/trips:nextActive/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicles:trips:nextactive.md): For each vehicle id in the body, returns its draft, requested, accepted and confirmed trips (not yet in transit) as lightweight trip summaries, ordered by start date. Trips are matched on the vehicle and on its attached chauffeurs and transport equipment. External (customer) users only see trips of their businesses.

offset/amount apply per vehicle (e.g. amount=1 returns the next trip of every vehicle). The request body is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicles:trips

Errors: 400 when the body is not valid JSON, has no vehicles array, or offset/amount is not an integer.

### Get sensor time series of a vehicle

 - [GET /vehicle/{id}/sensors/values/{fromDate}/{toDate}](https://docs.transportial.com/apis/vehicle/get:vehicle:sensorvalues.md): Returns a compact time series per sensor of the vehicle between two instants: every reported value with its time and unit. Use types to limit the payload to specific sensor types.

Permission: get:vehicle

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform. A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### List the tachograph times of the fleet

 - [GET /vehicles/tacho-times/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicles:tachotimes.md): Returns the current tachograph driving and remaining driving times per driver slot for one page of the tacho-equipped own fleet (vehicles with a tacho work-state sensor), ordered by name. The tacho requirement is part of the query, so the page and totalResults describe the same set.

Contract-managed (capacity) vehicles are excluded. The POST variant accepts an optional body with query (matched on name and license plate) and/or a vehicles id allow-list. amount is clamped to 1..100.

Permission: get:vehicle

Errors: 400 when offset is not an integer.

### Search the tachograph times of the fleet

 - [POST /vehicles/tacho-times/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/search:vehicles:tachotimes.md): Returns the current tachograph driving and remaining driving times per driver slot for one page of the tacho-equipped own fleet (vehicles with a tacho work-state sensor), ordered by name. The tacho requirement is part of the query, so the page and totalResults describe the same set.

Contract-managed (capacity) vehicles are excluded. The POST variant accepts an optional body with query (matched on name and license plate) and/or a vehicles id allow-list. amount is clamped to 1..100.

Permission: get:vehicle

### Get the trips and calendar events of a vehicle

 - [GET /vehicle/{id}/trips-and-events:byDateTime/{startDateTime}/{endDateTime}](https://docs.transportial.com/apis/vehicle/get:vehicle:trips-and-events:bydatetime.md): Returns, for one vehicle, the trips whose start date falls inside the range and the calendar event occurrences planned on the vehicle (recurring events expanded). toDos is always empty here; use the trips-todos-and-events endpoint to include to-dos.

Not paginated.

Permission: get:vehicle:trips or get:vehicle:events

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform. A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### Record that the vehicle's engine started

 - [POST /vehicle/{uuid}/{lifeCycle}/startEngine](https://docs.transportial.com/apis/vehicle/create:vehicle:event:startengine.md): Creates a start-engine event for the vehicle at the given geo reference, in the lifecycle from the path (actual when the value is not a known lifecycle), and stores it on the vehicle's platform.

The body's geoReference is either a full geo reference object or the id of an existing geo reference.

Permission: create:vehicle

Errors: 400 when the body is not valid JSON or has no geoReference; 404 when the vehicle does not exist on your platform or the referenced geo reference id is unknown.

### Attach a chauffeur to a vehicle

 - [POST /vehicle/{id}/attach/chauffeur](https://docs.transportial.com/apis/vehicle/attach:chauffeur.md): Records an attach-chauffeur action on the vehicle. The body is an attach-chauffeur action whose chauffeur references the driver; vehicle is always set to the vehicle in the path.

Without lifeCycle (or with actual) the chauffeur is attached immediately. With lifeCycle: planned and a startTime, the action is stored as planned and executed by the planned-action job once its start time is reached. When the platform's availability enforcement is BLOCK, a chauffeur with a blocking activity (leave, sickness, ...) in the action's period is rejected. Saving triggers integrations listening for attach-chauffeur actions.

Permission: attach:chauffeur

Errors: 400 when the body's type is not attachChauffeur, a planned action has no startTime, or the chauffeur is unavailable under BLOCK enforcement; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### List the past trips of a vehicle

 - [GET /vehicle/{id}/trips:history/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicle:trips:history.md): Lists the completed and cancelled trips of a vehicle, most recently updated first. Trips are matched on the vehicle and on its attached chauffeurs and transport equipment. External (customer) users only see trips of their businesses.

Paginated with offset/amount; totalResults holds the total number of matches. The request body (usually empty) is stored as the caller's last search for this window, which scopes live updates.

Permission: get:vehicle:trips

Errors: 400 when offset or amount is not an integer; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### List the latest sensor values of the fleet

 - [GET /vehicles/sensors/latest/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/get:vehicles:sensorslatest.md): Returns, for one page of the own fleet, every vehicle with the last reported value of each of its sensors. Powers the fleet FMS dashboard.

The page is taken from the own fleet of your platform: contract-managed (capacity) vehicles are always excluded. The GET variant pages the whole own fleet; the POST variant accepts an optional body with a free-text query or a vehicles id allow-list (the allow-list wins). amount is clamped to 1..100.

Permission: get:vehicle

Errors: 400 when offset is not an integer.

### Search the latest sensor values of the fleet

 - [POST /vehicles/sensors/latest/{offset}/{amount}](https://docs.transportial.com/apis/vehicle/search:vehicles:sensorslatest.md): Returns, for one page of the own fleet, every vehicle with the last reported value of each of its sensors. Powers the fleet FMS dashboard.

The page is taken from the own fleet of your platform: contract-managed (capacity) vehicles are always excluded. The GET variant pages the whole own fleet; the POST variant accepts an optional body with a free-text query or a vehicles id allow-list (the allow-list wins). amount is clamped to 1..100.

Permission: get:vehicle

### Attach transport equipment to a vehicle

 - [POST /vehicle/{id}/attach/transportEquipment](https://docs.transportial.com/apis/vehicle/attach:transportequipment.md): Records an attach-transport-equipment action on the vehicle. The body's transportEquipment references the trailer or container; vehicle is always set to the vehicle in the path.

Without lifeCycle (or with actual) the equipment is attached immediately. With lifeCycle: planned and a startTime, the action is stored as planned and executed by the planned-action job once its start time is reached. Saving triggers integrations listening for attach-transport-equipment actions.

Permission: attach:transportEquipment

Errors: 400 when the body's type is not attachTransportEquipment or a planned action has no startTime; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Get the driven track of a vehicle

 - [GET /vehicle/{id}/locationUpdateEvents/{fromDate}/{toDate}](https://docs.transportial.com/apis/vehicle/get:vehicle:locationupdateevents.md): Returns the driven track of a vehicle between two instants as lean track points (time, lat/lon, speed, heading), for drawing the route on a map.

A point's time is its position time, falling back to the creation time for older rows. At most 500 points are returned, newest first. On a query error an empty list is returned.

Permission: get:vehicle:locationUpdateEvents

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform. A date that is not a valid ISO 8601 instant is not validated and fails with 500.

### Get the latest location of all vehicles

 - [GET /vehicles/location](https://docs.transportial.com/apis/vehicle/get:vehicles:location.md): Returns the latest known position of every operational, non-deleted, non-archived vehicle of your platform, with its current trip. External (customer) users only see the vehicles of their businesses.

Not paginated. On a query error an empty list is returned.

Permission: Any authenticated user (no specific permission).

### Get the latest location of matching vehicles

 - [POST /vehicles/location](https://docs.transportial.com/apis/vehicle/search:vehicles:location.md): Returns the latest known position of every vehicle matching the search filters (same filters as POST /vehicles/{offset}/{amount}), with its current trip. An empty body behaves like the GET variant.

Not paginated: all matches are returned. Uses the vehicle search index: deleted and archived vehicles are never returned and external (customer) users only see the vehicles of their businesses. If the search itself fails, an empty page is returned instead of an error.

Permission: Any authenticated user (no specific permission).

Errors: 400 when the body is not valid JSON.

### List the sensors of a vehicle

 - [GET /vehicle/{id}/sensors](https://docs.transportial.com/apis/vehicle/get:vehicle:sensors.md): Returns the sensors attached to a vehicle (fuel, odometer, temperature, tacho work state, ...), each with its latest value. Powers the sensor picker of the FMS sensors tab. Not paginated.

Permission: get:vehicle

Errors: 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Detach transport equipment from a vehicle

 - [POST /vehicle/{id}/detach/transportEquipment](https://docs.transportial.com/apis/vehicle/detach:transportequipment.md): Records a detach-transport-equipment action on the vehicle. The body's transportEquipment references the trailer or container; vehicle is always set to the vehicle in the path.

Without lifeCycle (or with actual) the equipment is detached immediately. With lifeCycle: planned and a startTime, the action is stored as planned and executed by the planned-action job once its start time is reached.

Permission: detach:transportEquipment

Errors: 400 when the body's type is not detachTransportEquipment or a planned action has no startTime; 404 when no non-deleted, non-archived vehicle with this id exists on your platform.

### Record that the vehicle's engine stopped

 - [POST /vehicle/{uuid}/{lifeCycle}/stopEngine](https://docs.transportial.com/apis/vehicle/create:vehicle:event:stopengine.md): Creates a stop-engine event for the vehicle at the given geo reference, in the lifecycle from the path (actual when the value is not a known lifecycle), and stores it on the vehicle's platform.

The body's geoReference is either a full geo reference object or the id of an existing geo reference.

Permission: create:vehicle

Errors: 400 when the body is not valid JSON or has no geoReference; 404 when the vehicle does not exist on your platform or the referenced geo reference id is unknown.

### Record that the vehicle started waiting

 - [POST /vehicle/{uuid}/{lifeCycle}/startWaiting](https://docs.transportial.com/apis/vehicle/create:vehicle:event:startwaiting.md): Creates a start-waiting event for the vehicle at the given geo reference, in the lifecycle from the path (actual when the value is not a known lifecycle), and stores it on the vehicle's platform.

The body's geoReference is either a full geo reference object or the id of an existing geo reference.

Permission: create:vehicle

Errors: 400 when the body is not valid JSON or has no geoReference; 404 when the vehicle does not exist on your platform or the referenced geo reference id is unknown.

### Look up a license plate in a vehicle registry

 - [GET /vehicle/registry-lookup/{licensePlate}](https://docs.transportial.com/apis/vehicle/lookup:vehicle:registry.md): Looks up a license plate in a national vehicle registry (for example RDW for the Netherlands) to pre-fill the vehicle form. The registry is chosen from the platform's enabled vehicle-registry integrations: by country when given, otherwise by plate format.

Always answers 200: available is false when no enabled registry can handle the plate, and found is false when the registry has no record. Nothing is stored.

Permission: get:vehicle

### Record that the vehicle started moving

 - [POST /vehicle/{uuid}/{lifeCycle}/startMoving](https://docs.transportial.com/apis/vehicle/create:vehicle:event:startmoving.md): Creates a start-moving event for the vehicle at the given geo reference, in the lifecycle from the path (actual when the value is not a known lifecycle), and stores it on the vehicle's platform.

The body's geoReference is either a full geo reference object or the id of an existing geo reference.

Permission: create:vehicle

Errors: 400 when the body is not valid JSON or has no geoReference; 404 when the vehicle does not exist on your platform or the referenced geo reference id is unknown.

## VehicleGroup

A vehicle group is a named, platform-scoped set of vehicles used to filter the fleet in planners, maps and FMS dashboards. Members are referenced by vehicle id; updating a group replaces its whole member list. Deleting a group is a soft delete and does not affect the vehicles.

### List vehicle groups

 - [GET /vehicleGroups/{offset}/{amount}](https://docs.transportial.com/apis/vehiclegroup/get:vehiclegroups.md): Lists the non-deleted, non-archived vehicle groups of your platform with their vehicles, most recently updated first.

Paginated with offset/amount; totalResults holds the total number of matches.

Permission: get:vehicleGroups

Errors: 400 when offset or amount is not an integer.

### Search vehicle groups

 - [POST /vehicleGroups/{offset}/{amount}](https://docs.transportial.com/apis/vehiclegroup/search:vehiclegroups.md): Lists the vehicle groups of your platform. Despite the name, the request body is currently ignored: the result is the same page as GET /vehicleGroups/{offset}/{amount}.

Paginated with offset/amount; totalResults holds the total number of matches.

Permission: get:vehicleGroups

Errors: 400 when offset or amount is not an integer.

### Get a vehicle group

 - [GET /vehicleGroup/{id}](https://docs.transportial.com/apis/vehiclegroup/get:vehiclegroup.md): Returns one vehicle group of your platform with its member vehicles.

Permission: get:vehicleGroup

Errors: 404 when no non-deleted, non-archived vehicle group with this id exists on your platform.

### Delete a vehicle group

 - [DELETE /vehicleGroup/{id}](https://docs.transportial.com/apis/vehiclegroup/delete:vehiclegroup.md): Soft-deletes a vehicle group. The vehicles themselves are not affected. The deleted group is returned.

Permission: delete:vehicleGroup

Errors: 404 when no non-deleted, non-archived vehicle group with this id exists on your platform.

### List vehicle groups (ids only)

 - [GET /vehicleGroups/light](https://docs.transportial.com/apis/vehiclegroup/get:vehiclegroups:light.md): Returns every vehicle group of your platform in a minimal form: id, name and the ids of its vehicles. Intended for filter pickers that need all groups at once.

Not paginated. Groups without vehicles are not included.

Permission: get:vehicleGroups

### Update a vehicle group

 - [PUT /vehicleGroup](https://docs.transportial.com/apis/vehiclegroup/update:vehiclegroup.md): Replaces the name and member vehicles of the vehicle group identified by the body's id.

name is set as sent (blank when omitted) and vehicles replaces the full member list; entries are vehicle ids or objects with an id, unknown vehicles are skipped. When no group with this id exists on your platform, a new group is created instead.

Permission: update:vehicleGroup

Errors: 400 when the body is not valid JSON or has no id.

### Create a vehicle group

 - [POST /vehicleGroup](https://docs.transportial.com/apis/vehiclegroup/create:vehiclegroup.md): Creates a named group of vehicles, used to filter the fleet in planners, maps and FMS dashboards.

Only name and vehicles are read. Each entry of vehicles is a vehicle id or an object with an id; vehicles that do not exist on your platform are silently skipped.

Permission: create:vehicleGroup

Errors: 400 when the body is not valid JSON.

## Fleet

A fleet is a named, numbered group of vehicles. Each vehicle occupies a slot whose label (fleet number plus the slot number, e.g. TR01) acts as a virtual license plate that stays stable when the vehicle in the slot is replaced. Fleets are linked to teams and businesses; customer (external) accounts only see fleets of their own businesses in the compact list. Every save rebuilds the slots, and the history endpoint reconstructs from the audit trail which vehicle held each slot over time.

### List fleets

 - [GET /fleets/{offset}/{amount}](https://docs.transportial.com/apis/fleet/get:fleets:paginated.md): Returns one page of the non-deleted, non-archived fleets of your platform, most recently updated first, plus the total number of fleets in totalResults. Each fleet includes its vehicle slots, teams and businesses.

Permission: get:fleets

Errors: 400 when offset is not an integer.

### Search fleets

 - [POST /fleets/{offset}/{amount}](https://docs.transportial.com/apis/fleet/search:fleets.md): Full-text search over the non-deleted, non-archived fleets of your platform. Returns one page of matches, newest first, plus the total number of matches in totalResults.

- query: matched as a phrase against the fleet search string (name, number and the slot labels of its vehicles); a query containing * or | is parsed as a simple query string with AND semantics. Empty returns all fleets.

A failing search returns an empty page instead of an error.

Permission: get:fleets

Errors: 400 when the body is not a JSON object or offset/amount is not an integer.

### Update a fleet

 - [PUT /fleet](https://docs.transportial.com/apis/fleet/update:fleet.md): Replaces the name, number, businesses, teams and vehicle slots of the fleet identified by id in the body. The vehicle slots are rebuilt from the request, which records a new revision in the slot assignment history.

Vehicles are taken from vehicles: each entry references an existing vehicle by vehicle.id, entries are sorted by their order, unknown vehicle ids and duplicates are dropped, and the remaining vehicles get dense slot positions 0, 1, 2... Slot labels are the fleet number followed by the slot number counted from 1 and zero-padded to two digits (TR01, TR02, ...). Businesses and teams are linked by id; ids that do not exist on your platform are ignored.

When no active fleet with this id exists, the response contains a new, unsaved fleet built from the request and nothing is stored.

Permission: update:fleet

Errors: 400 when the body is not valid JSON, has no id, or a field has the wrong type.

### Create a fleet

 - [POST /fleet](https://docs.transportial.com/apis/fleet/create:fleet.md): Creates a fleet: a numbered group of vehicles whose positions act as virtual license plates.

Vehicles are taken from vehicles: each entry references an existing vehicle by vehicle.id, entries are sorted by their order, unknown vehicle ids and duplicates are dropped, and the remaining vehicles get dense slot positions 0, 1, 2... Slot labels are the fleet number followed by the slot number counted from 1 and zero-padded to two digits (TR01, TR02, ...). Businesses and teams are linked by id; ids that do not exist on your platform are ignored.

Permission: create:fleet

Errors: 400 when the body is not valid JSON or a field has the wrong type.

### List fleets with their vehicle ids

 - [GET /fleets/light](https://docs.transportial.com/apis/fleet/get:fleets.md): Returns a compact list of all non-deleted, non-archived fleets of your platform: only id, name and the ids of the vehicles in the fleet. Use it to populate pickers and to map vehicles to fleets without loading full fleets.

Not paginated. Fleets without any vehicle are left out. Users with an external account type (customer accounts) only see fleets linked to one of their businesses. A failing query returns an empty list.

Permission: get:fleets

### Get a fleet

 - [GET /fleet/{id}](https://docs.transportial.com/apis/fleet/get:fleet.md): Returns one fleet of your platform with its numbered vehicle slots, teams and businesses.

Archived and deleted fleets are not returned.

Permission: get:fleet

Errors: 404 when no fleet with this id exists on your platform (or it is archived or deleted).

### Delete a fleet

 - [DELETE /fleet/{id}](https://docs.transportial.com/apis/fleet/delete:fleet.md): Soft-deletes a fleet: it is flagged as deleted and no longer returned by list, search or get endpoints. The vehicles themselves are not changed.

Permission: delete:fleet

Errors: 404 when no fleet with this id exists on your platform (or it is archived or deleted).

### Get the slot assignment history of a fleet

 - [GET /fleet/{id}/history](https://docs.transportial.com/apis/fleet/get:fleet:history.md): Reconstructs from the audit trail which vehicle occupied each fleet slot (virtual license plate such as TR01) over time.

Slots are returned in slot order. Per slot, consecutive saves that keep the same vehicle are collapsed into one interval; a new interval starts when another vehicle takes over the slot, and an interval is closed when the slot is emptied. The interval of the current occupant has no to and current set to true. When the audit trail cannot be read the list is empty.

Permission: get:fleet

Errors: 404 when no fleet with this id exists on your platform (or it is archived or deleted).

## BoardComputer

A board computer is the telematics unit (or a mobile device acting as one) linked to a vehicle, transport equipment or chauffeur, together with the integration that feeds it. It is the source of positions, sensor values and driver activities, and the target for planned trips and messages. Board computers are soft-deleted and can be switched inactive.

### Get a board computer

 - [GET /boardComputer/{id}](https://docs.transportial.com/apis/boardcomputer/get:boardcomputer.md): Returns one board computer of your platform, including its linked vehicle, transport equipment, chauffeur, integration and identifiers.

Permission: get:boardComputer

Errors: 404 when no non-deleted, non-archived board computer with this id exists on your platform.

### Delete a board computer

 - [DELETE /boardComputer/{id}](https://docs.transportial.com/apis/boardcomputer/delete:boardcomputer.md): Soft-deletes a board computer: it is flagged as deleted and no longer returned by list, search or get endpoints. The deleted board computer is returned.

Permission: delete:boardComputer

Errors: 404 when no non-deleted, non-archived board computer with this id exists on your platform.

### Update a board computer

 - [PUT /boardComputer](https://docs.transportial.com/apis/boardcomputer/update:boardcomputer.md): Updates the board computer identified by the body's id.

Replace semantics for the editable fields: name (blank when omitted), type (defaults to vehicle), integration (reference by id; cleared when omitted or unknown), active (false when omitted) and boardComputerId (kept when omitted). Other fields in the body are ignored.

Permission: update:boardComputer

Errors: 400 when the body is not valid JSON or has no id; 404 when no board computer with this id exists on your platform.

### Create a board computer

 - [POST /boardComputer](https://docs.transportial.com/apis/boardcomputer/create:boardcomputer.md): Registers a board computer (an on-board telematics unit, or a mobile device acting as one) on your platform. Board computers link a vehicle, transport equipment or chauffeur to the integration that feeds its positions and telemetry.

The body is read as a board computer model; id is generated when omitted. Relations (vehicle, transportEquipment, chauffeur, integration) are given as references with an id.

Permission: create:boardComputer

Errors: 400 when the body is not valid JSON or a field has the wrong type.

### List board computers

 - [GET /boardComputers/{offset}/{amount}](https://docs.transportial.com/apis/boardcomputer/get:boardcomputers.md): Lists the non-deleted, non-archived board computers of your platform, most recently updated first.

Paginated with offset/amount; totalResults holds the total number of matches.

Permission: get:boardComputers (or create:boardComputers, which this list used to require)

Errors: 400 when offset or amount is not an integer.

### Search board computers

 - [POST /boardComputers/{offset}/{amount}](https://docs.transportial.com/apis/boardcomputer/search:boardcomputers.md): Full-text search over the board computers of your platform. The body's query is matched against the search index; an empty query returns all board computers.

Paginated with offset/amount; totalResults holds the total number of matches.

Permission: get:boardComputers

Errors: 400 when the body is not valid JSON or offset/amount is not an integer.

## Sensor

A sensor is a measuring device (temperature, humidity, fuel level, speed, ...) usually created by a board-computer integration and attached to a vehicle or transport equipment. Each stored reading is a sensor update event; to limit volume a reading is only stored when it changes beyond the platform's deadband or a heartbeat elapses. Readings can raise vehicle-issue alerts when they breach the sensor's constraints, and can be fetched raw by date range or as time buckets for charts.

### Get a sensor

 - [GET /sensor/{id}](https://docs.transportial.com/apis/sensor/get:sensor.md): Returns one sensor of your platform with its identifiers, constraints and latest reading (lastValueEvent).

Permission: get:sensor

Errors: 404 when no sensor with this id exists on your platform (or it is archived or deleted).

### Delete a sensor

 - [DELETE /sensor/{id}](https://docs.transportial.com/apis/sensor/delete:sensor.md): Permanently deletes a sensor together with its identifiers, constraints and all its stored readings (sensor update events). This cannot be undone. The response contains the sensor as it was before deletion.

Permission: delete:sensor

Errors: 404 when no sensor with this id exists on your platform (or it is archived or deleted).

### List the sensor types in use

 - [GET /sensor/types](https://docs.transportial.com/apis/sensor/get:sensors:types.md): Returns the distinct type values of all sensors on your platform (including archived and deleted sensors), for example to build a type filter. Not paginated; a failing query returns an empty list.

Permission: get:sensors

### Get the first sensor reading after a moment

 - [GET /sensor/{id}/value/{date}](https://docs.transportial.com/apis/sensor/get:sensor:value.md): Returns the first stored reading of the sensor strictly after date, i.e. the value the sensor reported next from that moment on. update is null when there is no later reading. Each reading includes its sensor.

Dates must be ISO 8601 instants (2026-10-01T00:00:00Z); an unparseable date currently fails with 500 instead of 400.

Permission: get:sensor:update

Errors: 404 when no sensor with this id exists on your platform (or it is archived or deleted).

### List sensor readings in a date range

 - [GET /sensors/{id}/updates/{start}/{end}](https://docs.transportial.com/apis/sensor/get:sensor:updates.md): Returns all stored readings of the sensor with a creation moment between start and end (inclusive), oldest first. Not paginated and not downsampled: a busy sensor can produce tens of thousands of readings per day, so use GET /sensors/{id}/aggregates/{start}/{end} for charts. Readings skipped by the write deadband are not stored and therefore not returned. A failing query returns an empty list.

Dates must be ISO 8601 instants (2026-10-01T00:00:00Z); an unparseable date currently fails with 500 instead of 400.

Permission: get:sensor:updates

Errors: 404 when no sensor with this id exists on your platform (or it is archived or deleted).

### List sensors

 - [GET /sensors/{offset}/{amount}](https://docs.transportial.com/apis/sensor/get:sensors:paginated.md): Returns one page of the non-deleted, non-archived sensors of your platform, most recently updated first, plus the total number of sensors in totalResults.

Permission: get:sensors

Errors: 400 when offset is not an integer.

### Search sensors

 - [POST /sensors/{offset}/{amount}](https://docs.transportial.com/apis/sensor/search:sensors.md): Full-text and faceted search over the non-deleted, non-archived sensors of your platform. Returns one page of matches plus the total number of matches in totalResults.

- query: matched as a phrase against name, type and placement; * or | switch to simple query string syntax.
- types: only sensors whose type equals one of the given values.
- createdAt: inclusive {start, end} range on the creation moment.
- sortBy: sort clauses on sortable index fields; without it results are ordered by relevance (with query) or newest first.

A failing search (for example sorting on an unsortable field) returns an empty page instead of an error.

Permission: get:sensors

Errors: 400 when the body is not a JSON object or offset/amount is not an integer.

### Update a sensor

 - [PUT /sensors](https://docs.transportial.com/apis/sensor/update:sensor.md): Updates the sensor identified by id in the body. Only the fields present in the body are changed; omitted fields keep their value. lastValueEvent is server-owned and ignored.

When no sensor with this id exists yet, a new sensor is created with the given fields (upsert). An id that belongs to another platform is rejected.

Permission: update:sensor

Errors: 400 when the body is not valid JSON, a field has the wrong type or validation fails; 404 when the id belongs to a sensor of another platform.

### Create a sensor

 - [POST /sensor](https://docs.transportial.com/apis/sensor/create:sensor.md): Creates a sensor: a measuring device (temperature, fuel level, speed, ...) whose readings are stored as sensor update events. Sensors are usually created by board-computer integrations and attached to a vehicle or transport equipment; readings cannot be posted through this endpoint and lastValueEvent is ignored.

Permission: create:sensor

Errors: 400 when the body is not valid JSON or a field has the wrong type.

### Get bucketed sensor values for charting

 - [GET /sensors/{id}/aggregates/{start}/{end}](https://docs.transportial.com/apis/sensor/get:sensor:aggregates.md): Groups the numeric readings of a sensor between start and end into fixed-width time buckets and returns average, minimum, maximum and sample count per bucket, oldest first. Use this instead of the raw updates endpoint for plotting.

- Range: when the range is wider than the platform's maximum chart span (FMS setting, default 90 days), start is moved forward so the most recent part is kept and rangeClamped is true.
- Resolution: the bucket width is chosen from a fixed ladder (1 s up to 1 week) so the response stays within the platform's data-point budget (default 1000) and never goes below its minimum resolution (default 60 s). A requested resolution is snapped up to the ladder and only honoured when it is not finer than the automatic choice; resolutionCoarsened tells when it was widened.
- Non-numeric readings are skipped; empty buckets are omitted. A failing query returns no buckets.

The response echoes the effective window and resolution. Dates must be ISO 8601 instants (2026-10-01T00:00:00Z); an unparseable date currently fails with 500 instead of 400.

Permission: get:sensor:updates

Errors: 404 when no sensor with this id exists on your platform (or it is archived or deleted).

## Tire

Tires mounted on vehicles or transport equipment: section width, aspect ratio, rim diameter, construction, service class, load index, speed rating and country of manufacture, plus notes and a linked invoice. Tires can be listed per vehicle or per transport equipment and searched by full text. Deleting is a soft delete.

### Update a tire

 - [PUT /tire](https://docs.transportial.com/apis/tire/update:tire.md): Updates the tire identified by id in the body. The body is merged onto the stored tire: fields that are left out keep their value. An id that does not exist yet creates a new tire.

Permission: update:Tire

Errors: 400 when the body is not valid JSON or contains an invalid value; 404 when the id belongs to a tire of another platform.

### Create a tire

 - [POST /tire](https://docs.transportial.com/apis/tire/create:tire.md): Creates a tire on your platform. The body is read directly into the tire entity; link it to a vehicle or transport equipment by sending an object with that entity's id.

Permission: create:Tire

Errors: 400 when the body is not valid JSON or contains an invalid value (for example an unknown tireClass or fabricType).

### List tires

 - [GET /tires/{offset}/{amount}](https://docs.transportial.com/apis/tire/get:tires.md): Returns one page of the tires of your platform, most recently updated first. Deleted and archived tires are not included.

Permission: get:Tires

Errors: 400 when offset is not an integer.

### Search tires

 - [POST /tires/{offset}/{amount}](https://docs.transportial.com/apis/tire/search:tires.md): Full-text search over the tires of your platform (search index). Deleted and archived tires are never returned. Results are ordered by relevance unless sortBy is given.

A failing search (for example an unsortable sortBy field) is logged and returns an empty page.

Permission: get:Tires

Errors: 400 when the body is not a JSON object or offset is not an integer.

### List tires of a transport equipment

 - [GET /tires/transportEquipment/{id}/{offset}/{amount}](https://docs.transportial.com/apis/tire/get:transportequipment:tires.md): Returns one page of the tires of your platform mounted on the given transport equipment, in no particular order. Deleted tires are not included; archived tires are. An unknown id returns an empty page.

Note: totalResults in this response is the requested page size (amount), not the number of matching tires.

Permission: get:TiresByTransportEquipment

Errors: 400 when offset is not an integer.

### List tires of a vehicle

 - [GET /tires/vehicle/{id}/{offset}/{amount}](https://docs.transportial.com/apis/tire/get:vehicle:tires.md): Returns one page of the tires of your platform mounted on the given vehicle, in no particular order. Deleted tires are not included; archived tires are. An unknown id returns an empty page.

Note: totalResults in this response is the requested page size (amount), not the number of matching tires.

Permission: get:TiresByVehicle

Errors: 400 when offset is not an integer.

### Get a tire

 - [GET /tire/{id}](https://docs.transportial.com/apis/tire/get:tire.md): Returns one tire of your platform by id. Deleted tires are not returned.

Permission: get:Tire

Errors: 404 when no tire with this id exists on your platform.

### Delete a tire

 - [DELETE /tire/{id}](https://docs.transportial.com/apis/tire/delete:tire.md): Soft-deletes a tire: it is marked deleted and no longer returned by lists, filters and searches.

Permission: delete:Tire

Errors: 404 when no tire with this id exists on your platform.

## Damage

A damage records what happened and when, with documents, and the vehicle, transport equipment, consignment and/or goods item it concerns. Damages are platform-scoped, soft-deleted, and listed per platform, per vehicle or per transport equipment. Damages are not in the search index, so the damage search endpoint returns nothing.

### List damages

 - [GET /damages/{offset}/{amount}](https://docs.transportial.com/apis/damage/get:damages.md): Returns a page of the damages of your platform, most recently updated first. Archived and deleted damages are left out.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:damages

Errors: 400 when offset is not a number.

### Search damages

 - [POST /damages/{offset}/{amount}](https://docs.transportial.com/apis/damage/search:damages.md): Intended to search the damages of your platform by query and sortBy. Damages are not in the search index, so this endpoint currently always returns an empty page with totalResults 0; use the list or filter endpoints instead.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:damages

Errors: 400 when the body is not a JSON object or offset is not a number.

### List damages of a transport equipment

 - [GET /damages/transportEquipment/{id}/{offset}/{amount}](https://docs.transportial.com/apis/damage/get:damages:transportequipment.md): Returns a page of the damages whose transportEquipment link has the given id, most recently updated first. Archived and deleted damages are left out; an unknown id gives an empty page. Note: the damage's transportEquipment link points to a vehicle record, so the id must be that record's id.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: filter:DamagesByTransportEquipment

Errors: 400 when offset is not a number.

### Update a damage

 - [PUT /damage](https://docs.transportial.com/apis/damage/update:damage.md): Updates the damage identified by the id in the body. Fields present in the body overwrite the stored values; fields left out keep their current value. When the id is not known on any platform, the body is stored as a new damage.

Permission: update:damage

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a damage of another platform.

### Create a damage

 - [POST /damage](https://docs.transportial.com/apis/damage/create:damage.md): Records a damage: a name, description and the moment it happened, with photos or other documents, and the vehicle, transport equipment, consignment and/or goods item it concerns. All fields are optional. Returns the stored damage.

Permission: create:damage

Errors: 400 when the body is not valid JSON or does not match the damage model.

### Get a damage

 - [GET /damage/{id}](https://docs.transportial.com/apis/damage/get:damage.md): Returns one damage of your platform by id. Archived and deleted damages are not found.

Permission: get:damage

Errors: 404 when no damage with this id exists on your platform.

### Delete a damage

 - [DELETE /damage/{id}](https://docs.transportial.com/apis/damage/delete:damage.md): Soft-deletes a damage: it is marked deleted and no longer returned by the damage endpoints. Returns the deleted damage.

Permission: delete:damage

Errors: 404 when no damage with this id exists on your platform.

### List damages of a vehicle

 - [GET /damages/vehicle/{id}/{offset}/{amount}](https://docs.transportial.com/apis/damage/get:damages:vehicle.md): Returns a page of the damages recorded against the given vehicle, most recently updated first. Archived and deleted damages are left out; an unknown id gives an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: filter:DamagesByVehicle

Errors: 400 when offset is not a number.

## Tachograph

Tachograph tools for driver card and vehicle unit downloads in the binary EU format. The convert endpoint parses an uploaded file in memory and returns its decoded blocks as JSON, without storing it. Remote downloads that integrations fetch (rFMS, FleetAccess) are stored as documents and indexed for the legal download intervals (driver card every 28 days, vehicle unit every 90 days).

### Convert a tachograph file to JSON

 - [POST /tachograph/convert/binary](https://docs.transportial.com/apis/tachograph/convert:tachograph.md): Parses an uploaded tachograph download (driver card or vehicle unit file, multipart part file) and returns its decoded contents as JSON. The file is processed in memory and is not stored, and nothing is attached to a driver or vehicle.

Failures are returned with HTTP 200 and only success: false: when the request has no file part or the file cannot be parsed.

Permission: convert:tachograph

## FuelTransaction

Fuel and energy-card transactions (liquid fuel, AdBlue, gas or electric charging) imported from card providers such as DKV, UTA, Shell or E100, or entered by hand. Each transaction can reference a vehicle and a location, and can be attributed to a trip and a chauffeur by id. On every save the server compares the fuelled quantity with the vehicle's tank capacity and flags the transaction as suspicious when it is larger; flagged transactions can be filtered in the search.

### Create a fuel transaction

 - [POST /fuel-transactions](https://docs.transportial.com/apis/fueltransaction/create:fuel-transaction.md): Records a fuel or energy-card transaction, for example one imported from a card provider (DKV, UTA, Shell, E100) or entered by hand.

On save the server runs the tank-capacity check: when the transaction is not already flagged and its quantity is larger than the referenced vehicle's fuelCapacity, flagged is set to true and flagReason explains why. The new transaction belongs to your platform.

Permission: create:fuel-transaction

Errors: 400 when the body is not valid JSON or does not match the schema.

### Get a fuel transaction

 - [GET /fuel-transaction/{id}](https://docs.transportial.com/apis/fueltransaction/get:fuel-transaction.md): Returns one fuel transaction of your platform by id.

Permission: get:fuel-transaction

Errors: 404 when no non-archived, non-deleted fuel transaction with this id exists on your platform.

### Update a fuel transaction

 - [PUT /fuel-transaction/{id}](https://docs.transportial.com/apis/fueltransaction/update:fuel-transaction.md): Updates a fuel transaction, for example to attribute it to a trip or chauffeur, correct the quantity, or set or clear the flagged state.

The transaction is identified by the id in the body. Fields present in the body overwrite the stored values; omitted fields keep theirs. An id that does not exist on any platform creates a new transaction. The tank-capacity check runs again on save when the transaction is not flagged.

Permission: update:fuel-transaction

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to another platform.

### Delete a fuel transaction

 - [DELETE /fuel-transaction/{id}](https://docs.transportial.com/apis/fueltransaction/delete:fuel-transaction.md): Permanently removes a fuel transaction of your platform and returns it as it was before deletion.

Permission: delete:fuel-transaction

Errors: 404 when no non-archived, non-deleted fuel transaction with this id exists on your platform.

### Search fuel transactions

 - [POST /fuel-transactions/{offset}/{amount}](https://docs.transportial.com/apis/fueltransaction/search:fuel-transactions.md): Lists the non-archived, non-deleted fuel transactions of your platform, newest transactionDate first (then newest createdAt), optionally filtered on trip and flag state.

The body is optional: an empty body or {} returns all transactions. total in the response is the number of matches across all pages.

Permission: get:fuel-transaction

Errors: 400 when the body is not valid JSON.

## MaintenancePlan

Recurring maintenance obligations (periodic service, APK, tachograph calibration, ...) of a vehicle or transport equipment, due by kilometres, engine hours and/or days since they were last performed. The server derives each plan's due state (`ok`, `dueSoon`, `overdue`, `unknown`) from the subject's odometer and engine-hour sensors on every save and in an hourly check; becoming due raises opt-in built-in alerts and fires the `afterMaintenanceDueSoon` / `afterMaintenanceOverdue` triggers. Requires the `maintenance` feature.

### Search maintenance plans

 - [POST /maintenance-plans/{offset}/{amount}](https://docs.transportial.com/apis/maintenanceplan/search:maintenanceplans.md): Lists the non-deleted maintenance plans of your platform, most urgent first: overdue, dueSoon, ok, then unknown, each ordered by nextDueAt (soonest first, plans without a due date last). The optional body filters on text, subject, status and active flag; total counts the matches across all pages.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: get:maintenancePlans

Errors: 400 with maintenance_disabled, or when the body is not valid JSON.

### Create a maintenance plan

 - [POST /maintenance-plans](https://docs.transportial.com/apis/maintenanceplan/create:maintenanceplan.md): Creates a recurring maintenance obligation for exactly one vehicle or transport equipment of your platform. The server derives status, nextDueAt, nextDueKm and nextDueEngineHours from the baseline (lastPerformed*), the intervals, the warn-before margins (or the platform defaults in maintenanceSettings) and the subject's latest odometer and engine-hour readings. A plan created dueSoon or overdue raises the opted-in built-in alert and fires afterMaintenanceDueSoon / afterMaintenanceOverdue.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: create:maintenancePlan

Errors: 400 with maintenance_disabled, maintenance_plan_subject_required (not exactly one of vehicle and transportEquipment), maintenance_plan_interval_required (no interval), or when name is blank or a value cannot be parsed; 404 when a referenced vehicle or transport equipment does not exist on your platform.

### Get a maintenance plan

 - [GET /maintenance-plan/{id}](https://docs.transportial.com/apis/maintenanceplan/get:maintenanceplan.md): Returns one maintenance plan of your platform, including currentKm and currentEngineHours: the subject's latest odometer (converted to km) and engine-hour readings, falling back to the highest readings of its completed work orders.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: get:maintenancePlan

Errors: 400 with maintenance_disabled; 404 when no non-deleted plan with this id exists on your platform.

### Update a maintenance plan

 - [PUT /maintenance-plan/{id}](https://docs.transportial.com/apis/maintenanceplan/update:maintenanceplan.md): Partially updates the plan with the id in the path: fields present in the body overwrite the stored values (send null to clear one), omitted fields keep theirs, and server-owned fields are ignored. The due state is derived again; a change into dueSoon or overdue raises the opted-in alert and fires the matching trigger, and leaving that state (or deactivating the plan) solves its open alert.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: update:maintenancePlan

Errors: 400 with maintenance_disabled, maintenance_plan_subject_required, maintenance_plan_interval_required, or a value that cannot be parsed; 404 when the plan or a referenced vehicle or transport equipment does not exist on your platform.

### Delete a maintenance plan

 - [DELETE /maintenance-plan/{id}](https://docs.transportial.com/apis/maintenanceplan/delete:maintenanceplan.md): Soft-deletes a maintenance plan of your platform and solves its open maintenance alerts. Work orders that performed the plan keep their own data; the deleted plan is no longer listed on them.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: delete:maintenancePlan

Errors: 400 with maintenance_disabled; 404 when no non-deleted plan with this id exists on your platform.

## WorkOrder

Workshop visits of a vehicle or transport equipment: the maintenance plans they perform, the damages they repair, cost lines with a computed total, the workshop, an incoming invoice and documents. A scheduled work order can block its subject for planning through a calendar event; completing it resets the linked maintenance plans and fires `afterWorkOrderCompleted`. Requires the `maintenance` feature.

### Get a work order

 - [GET /work-order/{id}](https://docs.transportial.com/apis/workorder/get:workorder.md): Returns one work order of your platform with lean references to its subject, plans, damages, workshop, location and invoice, and its documents.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: get:workOrder

Errors: 400 with maintenance_disabled; 404 when no non-deleted work order with this id exists on your platform.

### Update a work order

 - [PUT /work-order/{id}](https://docs.transportial.com/apis/workorder/update:workorder.md): Partially updates the work order with the id in the path: fields present in the body overwrite the stored values, omitted fields keep theirs; plans, damages, documents and lines replace the stored lists when sent. The blocking calendar event follows the scheduled times and is removed once the work order is completed or cancelled. Changing the status to completed sets completedAt (now when empty), moves every linked plan's baseline forward to this visit (never back to an older one), derives the plans again and fires afterWorkOrderCompleted.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: update:workOrder

Errors: 400 with maintenance_disabled, maintenance_plan_subject_required, work_order_mixed_currency, or a value that cannot be parsed; 404 when the work order or a referenced entity does not exist on your platform.

### Delete a work order

 - [DELETE /work-order/{id}](https://docs.transportial.com/apis/workorder/delete:workorder.md): Soft-deletes a work order of your platform and removes its blocking calendar event. Plans it already completed keep their moved baseline.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: delete:workOrder

Errors: 400 with maintenance_disabled; 404 when no non-deleted work order with this id exists on your platform.

### Create a work order

 - [POST /work-orders](https://docs.transportial.com/apis/workorder/create:workorder.md): Creates a workshop visit for exactly one vehicle or transport equipment of your platform. totalCost is computed from the lines. With blocksVehicle and both scheduled times set, a blocking calendar event is created for the subject so it is unavailable for planning (calendarEventId). Creating it directly as completed completes it at once: the linked plans' baselines move forward and afterWorkOrderCompleted fires.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: create:workOrder

Errors: 400 with maintenance_disabled, maintenance_plan_subject_required, work_order_mixed_currency (lines in different currencies), or when title is blank, scheduledEnd is before scheduledStart or a value cannot be parsed; 404 when a referenced vehicle, transport equipment, plan, damage, workshop, location, invoice or document does not exist on your platform.

### Search work orders

 - [POST /work-orders/{offset}/{amount}](https://docs.transportial.com/apis/workorder/search:workorders.md): Lists the non-deleted work orders of your platform, newest first. The optional body filters on text, subject, damage, maintenance plan and status; total counts the matches across all pages.

Requires the maintenance feature on your platform; without it every maintenance endpoint answers 400 with maintenance_disabled.

Permission: get:workOrders

Errors: 400 with maintenance_disabled, or when the body is not valid JSON.

## ServiceCard

A service card is a card or pass held by a chauffeur, with a free-text type, description, scanned documents, an issue date and an expiry date. A chauffeur is required. Cards are soft-deleted and can be listed, searched or filtered per chauffeur.

### Update a service card

 - [PUT /serviceCard](https://docs.transportial.com/apis/servicecard/update:servicecard.md): Updates the service card identified by the id in the body. Fields present in the body overwrite the stored values; fields left out keep their current value. When the id is not known on any platform, the body is stored as a new card.

Permission: update:ServiceCard

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a service card of another platform.

### Create a service card

 - [POST /serviceCard](https://docs.transportial.com/apis/servicecard/create:servicecard.md): Creates a service card for a chauffeur: a card or pass with a name, free-text type, description, scanned documents, issue date and expiry date. chauffeur is required; issuedAt and validUntil default to the time of creation when omitted. Returns the stored card.

Permission: create:ServiceCard

Errors: 400 when the body is not valid JSON or does not match the service card model. A body without chauffeur cannot be stored and fails with an error response.

### Get a service card

 - [GET /serviceCard/{id}](https://docs.transportial.com/apis/servicecard/get:servicecard.md): Returns one service card of your platform by id, with its chauffeur and documents. Archived and deleted cards are not found.

Permission: get:ServiceCard

Errors: 404 when no service card with this id exists on your platform.

### Delete a service card

 - [DELETE /serviceCard/{id}](https://docs.transportial.com/apis/servicecard/delete:servicecard.md): Soft-deletes a service card: it is marked deleted and no longer returned by the service card endpoints. Returns the deleted card.

Permission: delete:ServiceCard

Errors: 404 when no service card with this id exists on your platform.

### List service cards of a chauffeur

 - [GET /serviceCards/chauffeur/{id}/{offset}/{amount}](https://docs.transportial.com/apis/servicecard/get:servicecards:chauffeur.md): Returns a page of the service cards of the given chauffeur, most recently updated first. Archived and deleted cards are left out; an unknown id gives an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:filteredServiceCardsByChauffeur

Errors: 400 when offset is not a number.

### List service cards

 - [GET /serviceCards/{offset}/{amount}](https://docs.transportial.com/apis/servicecard/get:servicecards.md): Returns a page of the service cards of your platform, most recently updated first. Archived and deleted cards are left out.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:ServiceCards

Errors: 400 when offset is not a number.

### Search service cards

 - [POST /serviceCards/{offset}/{amount}](https://docs.transportial.com/apis/servicecard/search:servicecards.md): Searches the service cards of your platform in the search index. Optional filters, combined with AND: query (phrase match with slop 10 on id, name, type, description, chauffeur, documents and validity dates) and chauffeurId. Results are sorted by sortBy when given, otherwise by relevance. Archived and deleted cards are left out. A search the index cannot run returns an empty page; this currently includes every search that sends chauffeurId (use the chauffeur filter endpoint instead).

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:ServiceCards

Errors: 400 when the body is not a JSON object or offset is not a number.

## Chauffeur

A chauffeur is a driver or other crew member (driver, conductor, captain, pilot) of a platform, with personal data, driving licence, ADR certificate, tachograph card and driving/resting time counters reported by tachograph and board-computer integrations. A chauffeur can be linked to a User account for the driver app; creating one with a nested user invites a confirmed CHAUFFEUR user by e-mail. Trips belong to a chauffeur directly or through the vehicle it is attached to, and the extended form adds the chauffeur's current vehicles and transport equipment. Activities (work, driving, available, rest, vacation, free, holiday, sick, unavailable) are logged per chauffeur; logging the current activity closes the previous one and updates lastActivity. Deletion is soft and refused for chauffeurs managed by a capacity contract.

### Update a chauffeur timeslot

 - [PUT /chauffeur/timeslot](https://docs.transportial.com/apis/chauffeur/update:chauffeur:timeslot.md): Updates the chauffeur activity (timeslot) identified by the id in the body. Fields present in the body overwrite the stored values; omitted fields keep their current value.

When the id is not known on any platform, a new activity with that id is created instead. Archived or deleted activities of your platform can be updated.

Permission: update:chauffeur:timeslot

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to an activity of another platform.

### Create a chauffeur timeslot

 - [POST /chauffeur/timeslot](https://docs.transportial.com/apis/chauffeur/create:chauffeur:timeslot.md): Allocates a timeslot in a chauffeur's schedule (stored as a chauffeur activity), for example a shift,
leave or a block reserved for a trip. Timeslots may recur; their occurrences can be overridden per
date with timeslot exceptions.

chauffeur.id is required and must exist on your platform. vehicle.id and trip.id are optional
references; an unknown vehicle or trip id is silently stored as no vehicle or no trip.

Permission: create:chauffeur:timeslot

Errors: 400 when the body is not valid JSON or a field has the wrong type or format; 404 when
chauffeur.id is missing or no chauffeur with that id exists on your platform.

### List vehicles of a chauffeur

 - [GET /chauffeur/{id}/vehicles](https://docs.transportial.com/apis/chauffeur/get:chauffeur:vehicles.md): Returns every vehicle on which this chauffeur is registered as one of the vehicle's chauffeurs (the vehicle's chauffeurs list). The list is not paginated; totalResults equals the number of vehicles returned.

When no chauffeur with this id exists on your platform the response is an empty list, not a 404.

Permission: get:chauffeur:vehicles

### Update a chauffeur

 - [PUT /chauffeur](https://docs.transportial.com/apis/chauffeur/update:chauffeur.md): Updates the chauffeur identified by the id in the body. Fields present in the body overwrite the stored values; omitted fields keep their current value. A picture sent as a file reference is resolved and set separately.

When the id is not known on any platform, a new chauffeur with that id is created instead. Archived or deleted chauffeurs of your platform can be updated.

Permission: update:chauffeur

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a chauffeur of another platform.

### Create a chauffeur

 - [POST /chauffeur](https://docs.transportial.com/apis/chauffeur/create:chauffeur.md): Creates a new chauffeur (driver or other crew member) on your platform. picture may reference an
already uploaded file by id.

When the body contains a user object, a user account is created and invited as well: user.email
is required, user.username defaults to firstname.lastname (lower-cased). The user is created as a
confirmed CHAUFFEUR user with the platform's CHAUFFEUR role, the chauffeur's preferred language (or
the default language) and the platform currency, and receives an e-mail invitation to set a password
(valid for 48 hours). When the username or e-mail is already taken, the response is an error body
(success: false, user_username_email_exists) with HTTP status 200 and nothing is created.

Permission: create:chauffeur

Errors: 400 when the body is not valid JSON, a field has the wrong type or format, or user is
given without an email.

### List trips of several chauffeurs by date

 - [POST /chauffeurs/trips:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/chauffeur/get:chauffeurs:trips.md): Returns, per requested chauffeur, the trips (lightweight form) that overlap the range from fromDate to toDate: trips that start on or before toDate and have no end date or end on or after fromDate.

A trip belongs to a chauffeur when the chauffeur is assigned to it directly or is a chauffeur of the trip's vehicle. Archived and deleted trips are excluded; users of an external account type only see trips linked to one of their businesses. Trips are ordered by start date. The result holds one entry per requested id; totalResults is the number of entries. The request is recorded in the user's search history.

Permission: get:chauffeurs:trips

Errors: 400 when the body is not valid JSON or lacks chauffeurs. A fromDate/toDate that is not an ISO-8601 instant currently fails with 500.

### Delete a chauffeur timeslot

 - [DELETE /chauffeur/timeslot/{id}](https://docs.transportial.com/apis/chauffeur/delete:chauffeur:timeslot.md): Deletes a timeslot allocation from a chauffeur's schedule and returns it as it was before deletion.

Permission: delete:chauffeur:timeslot

Errors: 404 when no chauffeur timeslot with this id exists on your platform.

### Get a chauffeur

 - [GET /chauffeur/{id}](https://docs.transportial.com/apis/chauffeur/get:chauffeur.md): Returns one chauffeur of your platform by id, with type, personal details, licences, tacho card, remaining
driving and resting times, contact details and linked user.

Permission: get:chauffeur

Errors: 404 when no chauffeur with this id exists on your platform.

### Delete a chauffeur

 - [DELETE /chauffeur/{id}](https://docs.transportial.com/apis/chauffeur/delete:chauffeur.md): Soft-deletes a chauffeur: the chauffeur is marked deleted and returned; its trips and activities
are kept. A linked user account is not deleted. Chauffeurs that are
managed by a capacity contract cannot be deleted here.

Permission: delete:chauffeur

Errors: 400 when the chauffeur is managed by a capacity contract (chauffeur_contract_managed);
404 when no chauffeur with this id exists on your platform.

### Get trip statistics of a chauffeur

 - [GET /chauffeur/{id}/trip-stats](https://docs.transportial.com/apis/chauffeur/read:chauffeur:tripstats.md): Returns two trip counters for the chauffeur: totalTrips, the number of trips on your platform the
chauffeur is assigned to (directly or through the trip's vehicle), and currentTrips, the number of those trips
with status IN_TRANSIT. Customer (external) accounts only count trips linked to one of their businesses.

Permission: read:chauffeur-trip-stats

Errors: 404 when no chauffeur with this id exists on your platform.

### Get a chauffeur's activities, events, trips and to-dos

 - [GET /chauffeur/{id}/activities-and-events:byDateTime/{fromDate}/{toDate}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:activities-and-events:bydatetime.md): Returns everything on a chauffeur's agenda in a time range, in one call:

- activities: chauffeur activities whose start lies between fromDate and toDate (inclusive);
- eventOccurrences: occurrences of calendar events for this chauffeur in the range, with recurring
events expanded and their exceptions applied (empty when expansion fails);
- trips: non-archived, non-deleted trips of your platform that start in the range and have this
chauffeur assigned directly or via the trip's vehicle, ordered by start date;
- toDos: to-dos linked to this chauffeur in the range.

There is no maximum range length and no pagination.

Permission: get:chauffeur:activities or get:chauffeur:events

Errors: 400 when toDate lies at least one full day (24 hours) before fromDate; 404 when no chauffeur with this id exists on
your platform. A fromDate or toDate that is not an ISO-8601 instant currently fails with 500.

### List transport equipment of a chauffeur

 - [GET /chauffeur/{id}/transportEquipments](https://docs.transportial.com/apis/chauffeur/get:chauffeur:transportequipments.md): Lists the transport equipment (trailers, containers and other equipment) attached to the vehicles
the chauffeur is assigned to. The chauffeur has no direct equipment link: the list is derived from the chauffeur's
vehicles. Not paged: totalResults equals the number of returned items. When no chauffeur with this id exists on
your platform the endpoint returns an empty list instead of an error.

Permission: get:chauffeur:transportEquipments

Errors: none specific to this endpoint.

### List open trips of a chauffeur

 - [GET /chauffeur/{id}/trips/future/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:trips:future.md): Lists the trips of the chauffeur that are still to be executed or in progress: status REQUESTED, ACCEPTED,
CONFIRMED, IN_TRANSIT or MODIFIED. A trip belongs to the chauffeur when the chauffeur is assigned to the trip
directly or to the trip's vehicle. Trips are ordered by creation time (oldest first).

Paged with offset/amount; totalResults holds the total number of matching trips. Customer (external)
accounts only see trips linked to one of their businesses. The request is recorded in the user's search history.

Permission: get:chauffeur:trips

Errors: 400 when offset is not an integer; 404 when no chauffeur with this id exists on your platform.

### Load planner data for several chauffeurs

 - [POST /chauffeurs/trips-activities-events:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:trips-activities-events:bydate.md): Batch endpoint for the chauffeur planner: returns, in one round-trip, everything shown for the given chauffeurs between fromDate and toDate.

- chauffeursTrips: per chauffeur, the trips (lightweight form) assigned to the chauffeur directly or via the trip's vehicle that overlap the range (start before toDate, no end or end after fromDate); archived and deleted trips are excluded, external users only see trips of their businesses.
- chauffeurActivities: per chauffeur, the activities overlapping the range, with recurring activities expanded into occurrences.
- chauffeursEvents: per chauffeur, the occurrences of recurring calendar events of that chauffeur in the range.
- chauffeursToDos: per chauffeur, the non-deleted todos related to that chauffeur whose deadline or creation date falls in the range.

The request is recorded in the user's search history.

Permission: get:chauffeur:trips

Errors: 400 when the body is not valid JSON or lacks chauffeurs. A fromDate/toDate that is not an ISO-8601 instant currently fails with 500.

### List chauffeurs

 - [GET /chauffeurs/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/get:chauffeurs.md): Returns a page of the chauffeurs of your platform. Archived and deleted chauffeurs are excluded. Results are ordered by last update (most recent first), then by id.

Pagination: offset/amount page through the result; totalResults holds the total number of chauffeurs.

Use POST /chauffeurs/{offset}/{amount} to search and filter.

Permission: get:chauffeurs

Errors: 400 when offset or amount is not a number.

### Search chauffeurs

 - [POST /chauffeurs/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/search:chauffeurs.md): Runs a full-text search over the chauffeurs of your platform. Archived and deleted chauffeurs are always excluded. All body fields are optional and combine with AND:

- query: free-text search; blank is ignored.
- availableFrom + availableTo (both required for the filter): excludes chauffeurs on a trip that is not cancelled or completed overlapping the window, and chauffeurs with a blocking activity (platform availabilityBlockingTypes, default vacation/holiday/sick/unavailable) overlapping it.
- activityTypes (array or comma-separated string) with activityFrom/activityTo: keeps only chauffeurs holding one of these activity types in the window (default window: now, open-ended).
- vehiclePlannedFrom/vehiclePlannedTo, withoutVehiclePlannedFrom/withoutVehiclePlannedTo: keep or exclude chauffeurs with a vehicle planned in the window.
- tripPlannedFrom/tripPlannedTo, withoutTripPlannedFrom/withoutTripPlannedTo: keep or exclude chauffeurs on a trip in the window.
- withoutVehicle (boolean): only chauffeurs not attached to any vehicle.
- capacityContract: yes/true (only with a capacity contract), no/false (only without), both/all (no filter).
- hasADR, hasDriverLicense, onlyOperational (booleans, applied when true).
- types: chauffeur types to match (any of).
- languages: language names the chauffeur must all have.
- sortBy: array of { "fields": [...], "direction": "asc" | "desc" }; default ordering is by relevance.

Window dates are ISO-8601 instants. When a filter fails internally (for example an unparsable date) the result is an empty page rather than an error.

Pagination: offset/amount page through the hits; totalResults holds the total hit count.

Permission: get:chauffeurs

Errors: 400 when the body is not valid JSON or offset/amount is not a number.

### List trips of a chauffeur

 - [GET /chauffeur/{id}/trips/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:trips.md): Returns a page of all trips of a chauffeur on your platform, regardless of status or date.

Trips match when the chauffeur is assigned to the trip directly or is a chauffeur of the trip's vehicle. Results are ordered by creation date (oldest first). Users of an external account type only see trips linked to one of their businesses. The request is recorded in the user's search history.

Pagination: offset/amount page through the result; totalResults holds the total number of matching trips.

Permission: get:chauffeur:trips

Errors: 404 when no chauffeur with this id exists on your platform; 400 when offset or amount is not a number.

### Get the driving-style score of a chauffeur

 - [GET /chauffeur/{id}/driving-style/{fromDate}/{toDate}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:drivingstyle.md): Computes a driving-style score for the chauffeur over the period between fromDate and toDate.
For every vehicle on your platform the chauffeur is assigned to, the speed, RPM, cruise-control, total-fuel and
odometer sensor readings in the period are analysed with your platform's driving-style settings; the per-vehicle
scores are then combined into one (overall and cruise weighted by sample count, counts and durations summed,
fuel efficiency recomputed from the summed distance and fuel). Vehicles without those sensors contribute no
samples. The score is computed on request and not stored.

Permission: get:chauffeur

Errors: 404 when no chauffeur with this id exists on your platform. A date that is not a valid ISO-8601
instant is not mapped to 400; it results in a server error.

### List timeslots of several chauffeurs

 - [POST /chauffeurs/timeslots](https://docs.transportial.com/apis/chauffeur/get:chauffeurs:timeslots.md): Returns the chauffeur activities (timeslots) of the given chauffeurs whose start lies between start and end (inclusive), grouped per chauffeur.

Only chauffeurs that have at least one activity in the period appear in the result; totalResults is the number of chauffeur groups. Recurring activities are returned as stored (their base row), not expanded into occurrences. The request is recorded in the user's search history.

Permission: get:chauffeurs

Errors: 400 when the body is not valid JSON or a date is not an ISO-8601 instant.

### List chauffeurs with vehicles

 - [GET /chauffeurs:extended/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/get:chauffeurs:extended.md): Returns a page of the chauffeurs of your platform in extended form. Each extended chauffeur is the chauffeur plus two computed lists: vehicles (the vehicles that list this chauffeur as one of their chauffeurs) and transportEquipments (the transport equipment attached to those vehicles).

Archived and deleted chauffeurs are excluded. Results are ordered by last update (most recent first), then by id.

Pagination: offset/amount page through the result; totalResults holds the total number of chauffeurs.

Permission: get:chauffeurs:extended

Errors: 400 when offset or amount is not a number.

### Search chauffeurs with vehicles

 - [POST /chauffeurs:extended/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/search:chauffeurs:extended.md): Same search as POST /chauffeurs/{offset}/{amount}, but returns the chauffeurs in extended form. Each extended chauffeur is the chauffeur plus two computed lists: vehicles (the vehicles that list this chauffeur as one of their chauffeurs) and transportEquipments (the transport equipment attached to those vehicles).

Runs a full-text search over the chauffeurs of your platform. Archived and deleted chauffeurs are always excluded. All body fields are optional and combine with AND:

- query: free-text search; blank is ignored.
- availableFrom + availableTo (both required for the filter): excludes chauffeurs on a trip that is not cancelled or completed overlapping the window, and chauffeurs with a blocking activity (platform availabilityBlockingTypes, default vacation/holiday/sick/unavailable) overlapping it.
- activityTypes (array or comma-separated string) with activityFrom/activityTo: keeps only chauffeurs holding one of these activity types in the window (default window: now, open-ended).
- vehiclePlannedFrom/vehiclePlannedTo, withoutVehiclePlannedFrom/withoutVehiclePlannedTo: keep or exclude chauffeurs with a vehicle planned in the window.
- tripPlannedFrom/tripPlannedTo, withoutTripPlannedFrom/withoutTripPlannedTo: keep or exclude chauffeurs on a trip in the window.
- withoutVehicle (boolean): only chauffeurs not attached to any vehicle.
- capacityContract: yes/true (only with a capacity contract), no/false (only without), both/all (no filter).
- hasADR, hasDriverLicense, onlyOperational (booleans, applied when true).
- types: chauffeur types to match (any of).
- languages: language names the chauffeur must all have.
- sortBy: array of { "fields": [...], "direction": "asc" | "desc" }; default ordering is by relevance.

Window dates are ISO-8601 instants. When a filter fails internally (for example an unparsable date) the result is an empty page rather than an error.

Pagination: offset/amount page through the hits; totalResults holds the total hit count.

Permission: get:chauffeurs:extended

Errors: 400 when the body is not valid JSON or offset/amount is not a number.

### Apply a bulk action to multiple chauffeurs

 - [POST /chauffeurs/bulk](https://docs.transportial.com/apis/chauffeur/bulk:chauffeur.md): Applies one action to every chauffeur id in ids:

- delete: soft-deletes each chauffeur (sets deleted). Chauffeurs managed by a capacity contract are skipped.
- operational: sets operational to value (defaults to true when value is omitted).

Ids that do not exist on your platform are skipped silently. The response lists the ids that were
actually changed and their count.

Permission: delete:chauffeur for delete, update:chauffeur for operational (and any other action)

Errors: 400 when the body is not valid JSON, ids is empty, or action is not delete or
operational.

### Get the chauffeur linked to a user

 - [GET /chauffeur/user/{id}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:byuserid.md): Returns the chauffeur of your platform that is linked to the given user account, for example to resolve the
chauffeur profile of the user logged in to the driver app. Only chauffeurs on your own platform are matched.

Permission: any authenticated user for their own user id; get:chauffeur to look up the chauffeur of another user.

Errors: 401 when looking up another user without get:chauffeur; 404 when no chauffeur on your platform is linked to this user.

### Log the current activity of a chauffeur

 - [POST /chauffeurs/{id}/activity](https://docs.transportial.com/apis/chauffeur/update:chauffeur:activity.md): Changes the chauffeur's live status while keeping the history: the chauffeur's last activity is closed (its end set to now, and its end kilometre reading set to kmsReading when given) if it is still open, and a new activity of activityType starting now is created and stored as the chauffeur's lastActivity.

The new activity takes its name from the activity type and copies remarks and description. When vehicleId is omitted the vehicle of the previous activity is carried over; when transportEquipmentIds is omitted its transport equipment is carried over (an empty array clears it). The start kilometre reading is kmsReading, else the previous activity's end or start reading, else 0.

Permission: update:chauffeur

Errors: 400 when activityType is not one of work, driving, available, rest, vacation, free, holiday, sick, unavailable (case-insensitive) or the body is malformed; 404 when no chauffeur with this id exists on your platform, or when vehicleId or one of transportEquipmentIds does not exist on your platform (nothing is changed then).

### List completed trips of a chauffeur

 - [GET /chauffeur/{id}/trips/past/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:trips:past.md): Lists the trips of the chauffeur with status COMPLETED. A trip belongs to the chauffeur when the chauffeur is
assigned to the trip directly or to the trip's vehicle. Trips are ordered by creation time (oldest first).

Paged with offset/amount; totalResults holds the total number of matching trips. Customer (external)
accounts only see trips linked to one of their businesses. The request is recorded in the user's search history.

Permission: get:chauffeur:trips

Errors: 400 when offset is not an integer; 404 when no chauffeur with this id exists on your platform.

### Delete a chauffeur activity

 - [DELETE /chauffeur/activity/{id}](https://docs.transportial.com/apis/chauffeur/delete:chauffeur:activity.md): Deletes a chauffeur activity and returns it as it was before deletion.

Permission: delete:chauffeur:activity or create:chauffeur:activity

Errors: 404 when no chauffeur activity with this id exists on your platform.

### List timeslots of a chauffeur in a period

 - [GET /chauffeur/{id}/timeslots/{fromDate}/{toDate}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:timeslots.md): Returns the chauffeur activities (timeslots such as driving, resting, leave or sickness) of one
chauffeur whose start lies between fromDate and toDate (inclusive). The range may span at most 7 days.

The request is recorded in the user's search history. When no chauffeur with this id exists on your platform the
endpoint returns an empty list instead of an error.

Permission: get:chauffeurs

Errors: 400 when the range between fromDate and toDate is longer than 7 days. A date that is not a valid
ISO-8601 instant is not handled and results in a server error.

### Create and invite a user for a chauffeur

 - [POST /chauffeur/{id}/assign-user](https://docs.transportial.com/apis/chauffeur/assign:user:chauffeur.md): Creates a new user account for an existing chauffeur and e-mails an invitation to set a password.

The user gets the chauffeur's first and last name, the given e-mail (lower-cased) and a username that
defaults to firstname.lastname (lower-cased) when omitted. It is created as a confirmed CHAUFFEUR
user with the platform's CHAUFFEUR role, the chauffeur's preferred language (or the default language)
and the platform currency. The invitation link is valid for 48 hours. The chauffeur is then linked to
the new user and returned.

When the username or e-mail is already taken, the response is an error body (success: false,
user_username_email_exists) with HTTP status 200 and nothing is created.

Permission: create:chauffeur

Errors: 400 when the chauffeur already has a user account or email is missing or blank; 404
when no chauffeur with this id exists on your platform.

### Check availability of several chauffeurs

 - [POST /chauffeurs/availability](https://docs.transportial.com/apis/chauffeur/get:chauffeurs:availability.md): Checks, for a batch of chauffeurs, whether a blocking chauffeur activity overlaps the period from from to to.

An activity overlaps when it starts before to and has no end or ends after from. Which activity types block is configured per platform in the planning settings (availabilityBlockingTypes); when that list is empty the defaults vacation, holiday, sick and unavailable apply. Trips are not taken into account.

Blank and duplicate ids are dropped. The response holds one entry per remaining id, in request order; for an unavailable chauffeur it carries the type, start and end of the earliest blocking activity. Ids that do not belong to your platform are reported as available.

Permission: get:chauffeurs

Errors: 400 when the body is not valid JSON or a date is not an ISO-8601 instant.

### List a chauffeur's activities in a date range

 - [GET /chauffeur/{id}/activities:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:activities.md): Returns the chauffeur activities (work, driving, rest, availability, leave, ...) of one chauffeur whose
start lies between fromDate and toDate (inclusive), plus their count. Ranges of 8 full days
(192 hours) or longer are rejected; there is no pagination.

When no chauffeur with this id exists on your platform, an empty list is returned.

Permission: get:chauffeur:activities

Errors: 400 when the range is 8 full days or longer. A fromDate or toDate that is not an ISO-8601
instant currently fails with 500.

### List trips of a chauffeur by status

 - [GET /chauffeur/{id}/trips:byStatus/{status}/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:trips:bystatus.md): Returns a page of the chauffeur's trips that currently have the given trip status.

Trips match when the chauffeur is assigned to the trip directly or is a chauffeur of the trip's vehicle. Results are ordered by creation date (oldest first). Users of an external account type only see trips linked to one of their businesses. The request is recorded in the user's search history.

status is matched case-insensitively against the trip status values (draft, requested, accepted, confirmed, in_transit, pending_completion, completed, cancelled, modified). An unknown value falls back to requested.

Pagination: offset/amount page through the result; totalResults holds the total number of matching trips.

Permission: get:chauffeur:trips

Errors: 404 when no chauffeur with this id exists on your platform; 400 when offset or amount is not a number.

### List a chauffeur's trip board rows by priority

 - [GET /chauffeur/{id}/trips/prioritized/board/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:trips:prioritized:board.md): Returns lean board rows (no full trip graph) for the trips the chauffeur is assigned to directly, in the
same priority order as GET /chauffeur/{id}/trips/prioritized/{offset}/{amount}: IN_TRANSIT first, then ACCEPTED,
CONFIRMED, REQUESTED, DRAFT, MODIFIED, PENDING_COMPLETION, COMPLETED and CANCELLED; within a status by start
date, then creation time. Archived and deleted trips are excluded.

The optional group selects one of four mutually exclusive tabs that together cover every trip status exactly once:
active (in transit), upcoming (draft, requested, accepted, confirmed, modified), history (pending completion,
completed) and cancelled. Without group all trips are returned.

Paged with offset/amount; totalResults holds the total number of matching trips. Customer (external) accounts only
see trips linked to one of their businesses. The request is recorded in the user's search history.

Permission: get:chauffeur:trips

Errors: 400 when offset is not an integer; 404 when no chauffeur with this id exists on your platform; an
unknown group value fails the request.

### List trips of a chauffeur in a period

 - [GET /chauffeur/{id}/trips:byDate/{fromDate}/{toDate}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:trips:bydate.md): Lists the non-archived, non-deleted trips of the chauffeur on your platform whose start date lies between
fromDate and toDate (both taken as midnight, inclusive), ordered by start date. A trip belongs to the chauffeur
when the chauffeur is assigned to the trip directly or to the trip's vehicle. Not paged: totalResults equals the
number of returned trips. The request is recorded in the user's search history.

Permission: get:chauffeur:trips

Errors: 404 when no chauffeur with this id exists on your platform. A date not in yyyy-MM-dd format is not
mapped to 400; it results in a server error.

### Create a chauffeur activity

 - [POST /chauffeur/activity](https://docs.transportial.com/apis/chauffeur/create:chauffeur:activity.md): Registers a chauffeur activity (work, driving, rest, available, vacation, sick, ...) that has happened
or is planned, for example from a tachograph or a manual registration. The body is stored as a new
chauffeur activity as sent; referenced chauffeur, vehicle and transport equipment are taken from the
body by id. Use POST /chauffeurs/{id}/activity to switch a chauffeur's current activity instead.

Permission: create:chauffeur:activity

Errors: 400 when the body is not valid JSON or a field has the wrong type or format.

### List trips of a chauffeur by priority

 - [GET /chauffeur/{id}/trips/prioritized/{offset}/{amount}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:trips:prioritized.md): Lists the non-archived, non-deleted trips of the chauffeur in the requested scope (by default every trip except cancelled ones), ordered by how urgent they are for the driver:
IN_TRANSIT first, then ACCEPTED, CONFIRMED, REQUESTED, DRAFT, MODIFIED, followed by COMPLETED and
CANCELLED; within a status by start date, then creation time. Only trips the chauffeur is assigned to directly
are listed (not trips reached through the chauffeur's vehicle). Used by the driver app to show the current trip first.

Paged with offset/amount; totalResults holds the total number of matching trips. Customer (external)
accounts only see trips linked to one of their businesses. The request is recorded in the user's search history.

Permission: get:chauffeur:trips

Errors: 400 when offset is not an integer; 404 when no chauffeur with this id exists on your platform; an unknown scope value fails the request.

### Get a chauffeur with vehicles and equipment

 - [GET /chauffeur:extended/{id}](https://docs.transportial.com/apis/chauffeur/get:chauffeur:extended.md): Returns one chauffeur of your platform by id in the extended form: all chauffeur fields plus the vehicles the
chauffeur is assigned to and the transport equipment attached to those vehicles.

Permission: get:chauffeur:extended

Errors: 404 when no chauffeur with this id exists on your platform.

## Chauffeur Timeslots

Timeslots are chauffeur activities planned ahead, such as shifts and absences, stored as ChauffeurActivity records. A timeslot can recur daily, weekly, monthly, yearly or in a rotation of weeks on and off, repeating the UTC time window of its start and end. A single occurrence can be cancelled or changed through a date-based exception. Activity types that block planning (by default vacation, holiday, sick and unavailable) are configurable per platform and are used by search and the availability checks.

### Delete an exception of a chauffeur timeslot

 - [DELETE /chauffeur-timeslots/{timeslotId}/exceptions/{exceptionId}](https://docs.transportial.com/apis/chauffeur-timeslots/deletechauffeuractivityexception.md): Permanently deletes a date exception (cancellation or override) of a chauffeur timeslot,
so the regular recurrence applies again on that date.

The exception is looked up by exceptionId within your platform and must belong to the timeslot timeslotId.

Permission: write:chauffeur_timeslots

Errors: 404 when no exception with this id exists on your platform, or it does not belong to timeslot timeslotId.

### List exceptions of a chauffeur timeslot

 - [GET /chauffeur-timeslots/{timeslotId}/exception](https://docs.transportial.com/apis/chauffeur-timeslots/getchauffeuractivityexceptions.md): Intended to list the date-based exceptions (cancelled or rescheduled occurrences) of a recurring
chauffeur timeslot, ordered by exception date.

Not implemented yet: the server looks up the timeslot and queries its exceptions, but does not write a response
body. A successful call returns 200 with an empty body. Use
GET /chauffeurs/{chauffeurId}/timeslot-occurrences to read occurrences with their exceptions applied.

Permission: read:chauffeur_timeslots

Errors: 404 when no timeslot with this id exists on your platform.

### Add or update an exception on a chauffeur timeslot

 - [POST /chauffeur-timeslots/{timeslotId}/exception](https://docs.transportial.com/apis/chauffeur-timeslots/addtimeslotexception.md): Overrides a single date of a (recurring) chauffeur timeslot: either cancels that day or
moves/renames it (new start/end time, title, description, location).

The exception is upserted per timeslot and date: if an exception already exists for exceptionDate
it is overwritten with the values in the body, otherwise a new one is created. isCanceled defaults
to true when omitted, and exceptionDate defaults to today. Occurrence expansion of the timeslot
applies these exceptions.

Permission: update:calendar_event

Errors: 400 when the body is not valid JSON or a date/time cannot be parsed; 404 when no
chauffeur timeslot with this id exists on your platform.

## DriverReview

Internal reviews of chauffeurs written by planners or fleet managers: a score from 1 to 10 (shown as five stars with half steps), an optional platform-defined category (a Type with targetEntityType DRIVER_REVIEW_CATEGORY) and a comment. The server resolves the chauffeur and category on the caller's platform and always records the authenticated user as author on create and update. Deleting a review marks it as deleted. Reviews can be listed, searched or filtered per chauffeur.

### Update a driver review

 - [PUT /driver-review](https://docs.transportial.com/apis/driverreview/update:driverreview.md): Merges the request body into the stored review identified by the body id; fields left out keep their stored value. The chauffeur and category references are resolved again on your platform and user is set to the authenticated user. When no review with that id exists yet, it is created with that id.

Permission: update:DriverReview

Errors: 400 when the body is not valid JSON, the score is outside 1..10, or the category is not a DRIVER_REVIEW_CATEGORY type; 404 when the id belongs to a review of another platform, or the chauffeur or category does not exist on your platform.

### Create a driver review

 - [POST /driver-review](https://docs.transportial.com/apis/driverreview/create:driverreview.md): Creates an internal review of a chauffeur. The chauffeur and category references are resolved by id on your platform, and the author (user) is always set to the authenticated user. The score must be between 1 and 10.

Permission: create:DriverReview

Errors: 400 when the body is not valid JSON, the score is outside 1..10, or the category is not a DRIVER_REVIEW_CATEGORY type; 404 when the chauffeur or category does not exist on your platform.

### Get a driver review

 - [GET /driver-review/{id}](https://docs.transportial.com/apis/driverreview/get:driverreview.md): Returns one driver review of your platform.

Permission: get:DriverReview

Errors: 404 when no review with this id exists on your platform (deleted and archived reviews are not found).

### Delete a driver review

 - [DELETE /driver-review/{id}](https://docs.transportial.com/apis/driverreview/delete:driverreview.md): Marks the driver review as deleted. It no longer appears in GET, lists or searches. Returns the deleted review.

Permission: delete:DriverReview

Errors: 404 when no review with this id exists on your platform.

### List driver reviews

 - [GET /driver-reviews/{offset}/{amount}](https://docs.transportial.com/apis/driverreview/get:driverreviews.md): Returns one page of the driver reviews of your platform, most recently updated first. Deleted and archived reviews are left out.

Permission: get:DriverReviews

Errors: 400 when offset is not an integer.

### Search driver reviews

 - [POST /driver-reviews/{offset}/{amount}](https://docs.transportial.com/apis/driverreview/search:driverreviews.md): Searches the driver reviews of your platform. All filters are optional and combined with AND: query is matched as a phrase (slop 10) against the category label, score, comment and the chauffeur's name; chauffeurId, categoryId and userId match exactly. Results are ordered by relevance unless sortBy is given. Deleted and archived reviews are left out. When the search itself fails (for example sorting on a field that is not sortable) an empty page is returned.

Permission: get:DriverReviews

Errors: 400 when the body is missing or not valid JSON, or offset is not an integer.

### List driver reviews of a chauffeur

 - [GET /driver-reviews/chauffeur/{id}/{offset}/{amount}](https://docs.transportial.com/apis/driverreview/get:driverreviews:chauffeur.md): Returns one page of the driver reviews of one chauffeur on your platform, most recently updated first. An unknown chauffeur id gives an empty page.

Permission: filter:DriverReviewsByChauffeur

Errors: 400 when offset is not an integer.

## OAuth

Connected apps: MCP clients such as Claude, ChatGPT, Cursor and VS Code connect to the Transportial MCP server (`/mcp`) through OAuth 2.1. The client registers itself and sends the user to `/oauth/authorize`; the user signs in and approves the connection on the consent screen, choosing one platform and the scopes `read` and/or `write`. The resulting tokens act as that user on that platform and never beyond the user's role, `read` limiting them to read-only operations; account-security and internal operations are never available to them. Users list and disconnect their connected apps here. Requires the mcpServer platform feature.

### Deny a connection request

 - [POST /oauth/authorization/{token}/deny](https://docs.transportial.com/apis/oauth/deny:oauth:authorization.md): Refuses the connection request and returns the client's redirect address carrying error=access_denied.

Permission: any user signed in with a normal login session; OAuth and API-key tokens are refused.

Errors: 401 for OAuth/API-key tokens; 404 when the request is unknown, already decided or expired.

### List my connected apps

 - [GET /oauth/grants](https://docs.transportial.com/apis/oauth/get:oauth:grants.md): Lists the caller's active connections to MCP clients, newest first, with the platform and scopes of each. Not paged.

Permission: any user signed in with a normal login session; OAuth and API-key tokens are refused.

Errors: 401 for OAuth/API-key tokens.

### Get a pending connection request

 - [GET /oauth/authorization/{token}](https://docs.transportial.com/apis/oauth/get:oauth:authorization.md): Returns the connection request an MCP client (Claude, ChatGPT, Cursor, ...) started at GET /oauth/authorize, for the consent screen: the client's self-declared name and links, where the browser returns afterwards, the requested scopes and the platforms the caller can connect it to.

Permission: any user signed in with a normal login session; OAuth and API-key tokens are refused.

Errors: 401 for OAuth/API-key tokens; 404 when the request is unknown, already decided or expired.

### Approve a connection request

 - [POST /oauth/authorization/{token}/approve](https://docs.transportial.com/apis/oauth/approve:oauth:authorization.md): Connects the client to the chosen platform for the caller with the chosen scopes and returns the client's redirect address carrying a single-use authorization code (valid for two minutes). The client then exchanges it at POST /oauth/token for tokens that act as the caller on that platform, never beyond the caller's role; read limits them to read-only operations.

Permission: any user signed in with a normal login session; OAuth and API-key tokens are refused.

Errors: 400 when a scope was not requested; 401 for OAuth/API-key tokens, or a platform the caller does not belong to or without the mcpServer feature; 404 when the request is unknown, already decided or expired.

### Disconnect a connected app

 - [DELETE /oauth/grant/{id}](https://docs.transportial.com/apis/oauth/delete:oauth:grant.md): Revokes one of the caller's connections: its refresh token stops working and every access token issued under it ends immediately.

Permission: any user signed in with a normal login session; OAuth and API-key tokens are refused.

Errors: 401 for OAuth/API-key tokens; 404 when the connection does not exist, is already revoked or belongs to another user.

## User

People who sign in to Transportial: planners, administrators, financial staff, customer-portal and supplier users and driver-app accounts. Covers sign-up (new self-service platform or access request awaiting admin approval), username/password and SAML login, two-factor authentication (TOTP), JWT sessions (24 h access token, 3-day refresh token), password reset and invitations, profile and region preferences, and admin actions such as block, delete and approve. Each user has one role, a primary platform plus optional extra platforms, and (for customer/supplier accounts) linked businesses that scope what they see.

### Get the logged-in user

 - [GET /user](https://docs.transportial.com/apis/user/get:user.md): Returns the user of the session making the request, including role, currency, avatar, businesses, identifiers,
notes, language and platforms. Used by the apps right after login.

Permission: Any authenticated user; no specific permission.

Errors: 403 when not authenticated; 404 when the session does not belong to a user (for example an API-access session).

### Update a user profile

 - [PUT /user](https://docs.transportial.com/apis/user/update:user.md): Updates the profile of the authenticated user, or, for administrators, of another user.

The target is the user with id from the body (default: yourself). Editing someone else requires a role level of at
least 1000 and that the user is a member of the platform of the request; otherwise the request fails with 404.

Everyone can change: firstName, lastName, username (lower-cased, not checked for uniqueness), avatar,
timezone, language and the region overrides measurementUnit, dateFormat, timeFormat, locale and currency
(only keys present in the body are touched; null or blank clears an override).

Only administrators (role level 1000 or higher) can change email, role, accountType, platforms (added to the
current memberships, never removed; only platforms the administrator can access, others are skipped) and businesses
(replaced); these keys are ignored for other callers. An
administrator must always send email, as an omitted e-mail clears the address. Status, two-factor settings, login
method and features are never changed here.

When a non-administrator sends an email that differs from the current one and is already used by a user of this
platform, the endpoint answers HTTP 200 with success: false and the message "Username and/or e-mail already exists".

Permission: Any authenticated user; no specific permission (see the administrator rules above).

Errors: 403 when not authenticated; 404 when the target user does not exist or you may not edit it.

### Create a user

 - [POST /user](https://docs.transportial.com/apis/user/create:user.md): Creates a confirmed user on your platform with the given role, without sending an e-mail and without a password:
the user sets one through POST /user/forgot (use POST /user/invite to e-mail an invitation instead).

Username and e-mail are stored in lower case. The language defaults to the platform default, the currency to the
platform currency, the account type to planning. The user becomes a member of the platform of the request plus the
platforms sent that you can access yourself (others are skipped), is linked to the businesses sent, and gets default
settings. Adding a user may be billed per seat.

When the username or e-mail address is already used by a user whose primary platform is this platform, the endpoint
answers HTTP 200 with success: false and the message user_username_email_exists.

Permission: create:user and a role level of at least 1000 (platform administrator).

Errors: 401 without the permission or level, or when the role is above your own role level; 402 when the
platform's free allowance of users is used up and billing approval or a payment method is needed (this endpoint does
not read hasAgreedToPrice); 403 when not authenticated; 404 when the role does not exist.

### Refresh an access token

 - [POST /user/session/refresh](https://docs.transportial.com/apis/user/refresh:user:session.md): Issues a new JWT access token for a session using its refresh token.

The session is found by its refresh_token as long as that has not expired, the session has not ended (logout, logout
of other sessions, blocking, failed two-factor code) and its user is not blocked. Its expiry is moved to 24 hours from now,
the refresh token's expiry to 3 days from now, and a new access_token is returned; the refresh token itself stays the same.

Permission: Public, no authentication (the refresh token is the credential).

Errors: 400 when refreshToken is missing or blank, and 400 (with success: false) when no open session has
this refresh token, it has expired, or the user is blocked.

### Disable two-factor authentication

 - [POST /user/2fa/disable](https://docs.transportial.com/apis/user/disable:user:2fa.md): Switches two-factor authentication off for the authenticated user and removes the TOTP secret and recovery codes.
When two-factor authentication is on, the request must prove the account holder is present with a current TOTP token
or the user's password. Without either it is only accepted from a session that started less than
TWO_FACTOR_DISABLE_GRACE_SECONDS ago (server setting, 15 minutes by default, 0 means always ask). A setup that was
never verified can be cancelled without proof. The response has the same shape as the setup response, with an empty
secret's QR code and no recovery codes.

Permission: Any authenticated user; no specific permission.

Errors: 401 when two-factor authentication is on and neither a valid token nor the correct password is sent
from a session older than the grace period; 403 when not authenticated; 404 when the session does not belong to a user.

### Decline an access request

 - [POST /user/access-request/{id}/decline](https://docs.transportial.com/apis/user/decline:user:accessrequest.md): Declines a pending access request: the applicant receives a decline e-mail and the user is soft-deleted.

Permission: decline:user

Errors: 401 without the permission; 403 when not authenticated; 404 when no user with this id has your platform as primary platform.

### Log in with username and password

 - [POST /user/login](https://docs.transportial.com/apis/user/login:user.md): Signs a user in with username (or e-mail address) and password and opens a new session.

The user is looked up among the users of the platform of the request (primary platform or membership): first by exact
username, then by lower-cased e-mail address. On success the response carries the session with a JWT access_token
(valid 24 hours; send it as Authorization: Bearer ) and a refresh_token (valid 3 days, see
POST /user/session/refresh). For users with two-factor authentication the session is opened but cannot use any
authenticated endpoint until a valid code is sent to POST /user/login:otp with that access token.

Failed attempts answer HTTP 200 with success: false, no session and a translated message: unknown user or wrong
password (invalid_login), account not confirmed yet (user_not_confirmed, for example waiting for e-mail verification
or approval), a user that must sign in through SAML (invalid_login_method) or, after a correct password, a user blocked
by an administrator (user_blocked).

Permission: Public, no authentication. Rate limited to 10 requests per minute per IP address.

Errors: 429 when the rate limit is exceeded.

### Create a chauffeur for a user

 - [POST /user/{id}/create-chauffeur](https://docs.transportial.com/apis/user/create:chauffeur:user.md): Creates a new chauffeur from the body and links it to the user in the path, so that user logs in as
the new chauffeur. Unlike POST /chauffeur, no user account is created or invited: the existing user is
used. The user must not already be linked to a chauffeur on your platform.

Permission: create:chauffeur

Errors: 400 when the user is already linked to a chauffeur or the body is not valid JSON; 404
when no user with this id exists on your platform.

### Check whether an e-mail address is taken

 - [POST /user/email/exists/{email}](https://docs.transportial.com/apis/user/exists:user:email.md): Tells whether a user with exactly this e-mail address (case-sensitive) exists among the users whose primary platform
is the platform of the request.
Used by sign-up forms. The response contains only exists.

Permission: Public, no authentication. Rate limited to 10 requests per minute per IP address.

Errors: 429 when the rate limit is exceeded.

### Get a user

 - [GET /user/{id}](https://docs.transportial.com/apis/user/get:user:byid.md): Returns a user by id, including role, currency, avatar, businesses, identifiers and notes. Only users whose primary platform is the platform of the request can be addressed.

Permission: Any authenticated user; no specific permission.

Errors: 403 when not authenticated; 404 when no user with this id has your platform as primary platform.

### Delete a user

 - [DELETE /user/{id}](https://docs.transportial.com/apis/user/delete:user.md): Soft-deletes a user: the user is flagged deleted and disappears from lists and searches, and the billing usage of
the user's seat is released so it can be reused. Existing sessions are not ended by this call. Only users whose primary platform is the platform of the request can be addressed.

Permission: delete:user and a role level of at least 1000 (platform administrator).

Errors: 401 without the permission or without administrator level; 403 when not authenticated; 404 when no user
with this id has your platform as primary platform.

### List users

 - [GET /users/{offset}/{amount}](https://docs.transportial.com/apis/user/get:users.md): Returns one page of the users whose primary platform is the platform of the request (deleted and archived users
excluded), plus the total count. Users that are only members of your platform through platforms are not listed.

Permission: Any authenticated user; no specific permission.

Errors: 400 when offset is not a number; 403 when not authenticated.

### Search users

 - [POST /users/{offset}/{amount}](https://docs.transportial.com/apis/user/search:users.md): Searches the users whose primary platform is the platform of the request and returns one page of matches plus the
total number of matches, most recently updated first.

query is matched as a phrase (slop 10) against username, first name, last name, e-mail address and phone number;
an empty query matches everyone. With level only users whose role level is at least that value are returned (for
example 1000 for administrators). Deleted and archived users are excluded. When the search itself fails the error is
logged and an empty page with total 0 is returned.

Permission: search:users

Errors: 400 when the body is not valid JSON or offset is not a number; 401 without the permission; 403 when not authenticated.

### List all sessions of a user

 - [GET /user/{id}/sessions/{offset}/{amount}](https://docs.transportial.com/apis/user/get:user:sessions.md): Returns one page of all sessions of a user, open and ended, oldest first, plus the total count. Only users whose
primary platform is the platform of the request can be looked up.
Each session includes its IP address and device; refresh_token is always empty in this list.

Permission: The user themself, or a platform administrator (role level 1000 or higher) for users of the platform.

Errors: 401 when you ask for another user's sessions without being a platform administrator; 403 when not
authenticated; 404 when no user with this id has your platform as primary platform.

### Register a user (access request form)

 - [POST /user/register/request](https://docs.transportial.com/apis/user/register:user:request.md): Public sign-up. What happens depends on the body:

- New platform (type user, accountType not supplier, and businessInformation.companyName or a legacy
billingId set): creates a platform with a business, an operational-base location and a billing on the primary
(free-tier) plan. businessInformation.nameSpace is required: it becomes the platform's nameSpace and its host name
{nameSpace}.{portal domain} (returned as portalHostName). The registrant becomes its administrator (role ADMIN) and only member. A ticked
termsAndConditions is recorded as the platform's acceptance. With an e-mail address a verification e-mail is sent
and the account stays DEFAULT until verified; without one the account is CONFIRMED immediately.
- Supplier access request (accountType supplier): creates the user and the business on the platform of the
request, pending approval by an administrator (POST /user/access-request/{id}/approve).
- User access request (type user otherwise): creates the user on the platform of the request, pending approval.

Access requests get role USER (level 0), are not members of the platform until approved, cannot log in before then
(unless they registered without an e-mail address), and trigger a confirmation e-mail to the applicant and a
notification to the platform's contact address. Default settings are created from the platform's primary settings group,
and the password is stored hashed. accountType is stored as sent.

Validation failures answer HTTP 200 with success: false: username or e-mail already used by any user
(user_username_email_exists), password and repeatPassword differ (passwords_no_match), the namespace of a new
platform is invalid, reserved or taken (platform_namespace_invalid, platform_namespace_reserved, platform_namespace_taken)
or the reCAPTCHA check failed.

Same behaviour as POST /user/register; used by the access-request form of the login page.

Permission: Public, no authentication. Unlike POST /user/register no endpoint-specific rate limit applies.

Errors: 404 when the platform of the request cannot be determined for an access request.

### Re-send the e-mail confirmation link

 - [POST /user/verification/resend](https://docs.transportial.com/apis/user/resend:user:verification.md): Sends a new confirmation link for a self-service signup whose e-mail address is not confirmed yet. The user is
found by username, or by e-mail address ignoring case, across all platforms. Earlier links stay valid. Nothing is sent when the
account is already confirmed, was never sent a confirmation link (access requests wait for admin approval instead) or was sent
one less than VERIFICATION_RESEND_COOLDOWN_SECONDS (default 60) ago.

The answer is the same in every case, so the endpoint does not reveal whether an account exists.

Permission: Public, no authentication. Rate limited to 5 requests per minute per IP address.

Errors: 429 when the rate limit is exceeded.

### Set a new password with a reset token

 - [POST /user/recover](https://docs.transportial.com/apis/user/recover:user:password.md): Sets a new password with the id and token from a password-reset e-mail (POST /user/forgot, valid 3 hours) or an
invitation e-mail (valid 48 hours). The token can be used once. Existing sessions are not ended.

Failures answer HTTP 200 with success: false: password and repeatPassword differ (passwords_no_match) or the
token is wrong or expired (token_expired).

Permission: Public, no authentication (the reset id and token are the credential). Rate limited to 10 requests per minute per IP address.

Errors: 404 when no reset request with this id exists; 429 when the rate limit is exceeded.

### Register a device for push notifications

 - [POST /user/fcm](https://docs.transportial.com/apis/user/create:user:fcm.md): Registers a Firebase Cloud Messaging token for the authenticated user so that push notifications (for example of
the mobile apps) reach that device. When the token is already registered on this platform nothing is added and the
response is { "success": false }.

Permission: fcm:user

Errors: 401 without the permission; 403 when not authenticated; 404 when the session does not belong to a user.

### Unblock a user

 - [POST /user/unblock/{id}](https://docs.transportial.com/apis/user/unblock:user.md): Clears the locked flag of a previously blocked user. Sessions ended by blocking stay ended. Only users whose primary platform is the platform of the request can be addressed.

Permission: unblock:user

Errors: 401 without the permission; 403 when not authenticated; 404 when no user with this id has your platform as primary platform.

### Disable two-factor authentication with a recovery code

 - [POST /user/2fa/recover](https://docs.transportial.com/apis/user/recover:user:2fa.md): Switches two-factor authentication off when the given recovery code is one of the authenticated user's recovery
codes (case and surrounding spaces are ignored); the secret and all recovery codes are removed, so a code works once.
Returns whether the code matched.

Meant for users who lost their authenticator: it also accepts a session that is still waiting for its two-factor code
after POST /user/login. When the code matches, that session counts as having passed two-factor authentication, so it
can be used right away. Limited to 10 requests per minute.

Permission: Any logged-in user session, also one still waiting for its two-factor code; no specific permission.

Errors: 400 when the body is not valid JSON; 403 when the request has no user session (no or an expired,
logged-out or blocked session token); 429 when the rate limit is exceeded.

### Get how a user signs in

 - [POST /user/login-method](https://docs.transportial.com/apis/user/get:user:loginmethod.md): Tells the login screen whether a user signs in with a password or through SAML single sign-on, and which platform to brand the login with.

The user is looked up across all platforms by exact username, then by lower-cased e-mail address. When the user signs in
through SAML, or is unknown but the domain of the entered address matches an enabled SAML configuration of the platform
of the request, a 90-second login attempt is started and the response contains loginMethod: SAML and the identity
provider redirectURL. Otherwise loginMethod is DEFAULT (also for unknown users).

The platform is the platform of the request when the user is a member of it, otherwise the user's primary platform;
its extended branding fields are merged into the response at the top level.

Permission: Public, no authentication. Rate limited to 10 requests per minute per IP address.

Errors: 404 when the user must sign in through SAML but no enabled SAML configuration of this platform allows the
user's e-mail domain; 429 when the rate limit is exceeded; 500 when the SAML authentication request cannot be built.

### Check whether a username is taken

 - [POST /user/username/exists/{username}](https://docs.transportial.com/apis/user/exists:user:username.md): Tells whether a user with exactly this username exists among the users whose primary platform is the platform of
the request. Used by sign-up forms.
The response contains only exists.

Permission: Public, no authentication. No endpoint-specific rate limit.

Errors: none.

### Log out all other sessions

 - [GET /user/logout/all](https://docs.transportial.com/apis/user/logout:user:all.md): Ends every other open session of the authenticated user (for example on other devices); the session used for this
request stays open. The ended sessions' access and refresh tokens stop working immediately. Returns the sessions that
were ended, with an empty refresh_token. totalResults is not filled (always 0).

Permission: Any authenticated user; no specific permission.

Errors: 403 when not authenticated.

### Approve an access request

 - [POST /user/access-request/{id}/approve](https://docs.transportial.com/apis/user/approve:user:accessrequest.md): Approves a pending access request: the user becomes CONFIRMED, is added as a member of their primary platform and
receives an approval e-mail. The role (USER) and account type chosen at sign-up are kept; change them with PUT /user.

Permission: approve:user

Errors: 401 without the permission; 403 when not authenticated; 404 when no user with this id has your platform
as primary platform, or the user's primary platform does not exist.

### Verify a two-factor code

 - [POST /user/2fa/verify](https://docs.transportial.com/apis/user/verify:user:2fa.md): Checks a code from the authenticator app against the authenticated user's TOTP secret. The first valid code after
POST /user/2fa/setup switches two-factor authentication on: from the next login the user must also pass
POST /user/login:otp. Returns whether the code was valid.

Permission: Any authenticated user; no specific permission.

Errors: 403 when not authenticated; 404 when the session does not belong to a user.

### Request a password reset e-mail

 - [POST /user/forgot](https://docs.transportial.com/apis/user/forgot:user:password.md): Starts a password reset: creates a reset token valid for 3 hours and e-mails the user a link containing the reset
id and token (through the platform's e-mail integration). Complete the reset with POST /user/recover.

The e-mail address is matched ignoring case against users of the platform of the request (primary platform or membership).

Permission: Public, no authentication. Rate limited to 10 requests per minute per IP address.

Errors: 404 when no such user exists; 429 when the rate limit is exceeded.

### Invite a user by e-mail

 - [POST /user/invite](https://docs.transportial.com/apis/user/invite:user.md): Creates a user (or updates an existing one) and e-mails an invitation link to set a password.

An existing user is matched by id, else username, else email (the first key present in the body); otherwise a new
user is created and gets the settings of the platform's primary settings group. The user is set to CONFIRMED, with
the given names, lower-cased username and e-mail, role, language (default: platform default), the platform currency,
account type (default planning), standardLoadingSize (default 20), extra platforms (only platforms you can access
yourself; others are skipped) and businesses. The user
always becomes a member of the platform of the request.

A password-reset token valid for 48 hours is created and an invitation e-mail with the link is sent in the user's
language through the platform's e-mail integration. The user completes it with POST /user/recover.

Permission: invite:user

Errors: 401 without the permission, or when the role is above your own role level; 402 when the platform's free
allowance of users is used up and billing approval or a payment method is needed; 403 when not authenticated; 404
when the role does not exist.

### Verify an e-mail address

 - [GET /user/{id}/verify/{token}](https://docs.transportial.com/apis/user/verify:user:email.md): Confirms an e-mail address with the link from the verification e-mail.

The token is checked against the user's (first) e-mail verification record. When it matches, the record is marked
verified, the user's e-mail address is set to the verified address and an account in status DEFAULT becomes
CONFIRMED (it can log in). Returns the resulting status; success is false when the token does not match.

Permission: Public, no authentication (the token is the credential).

Errors: 404 when no user with this id exists.

### List pending access requests

 - [GET /user/access-requests/{offset}/{amount}](https://docs.transportial.com/apis/user/get:user:accessrequests.md): Returns one page of pending access requests of the platform of the request, oldest first, plus the total count:
users whose primary platform is this platform and whose status is still DEFAULT (registered through the access-request
or supplier sign-up and not yet approved or declined). Deleted and archived users are excluded.

Permission: search:users

Errors: 401 without the permission; 403 when not authenticated; 404 when the platform of the request cannot be determined.

### Complete login with a two-factor code

 - [POST /user/login:otp](https://docs.transportial.com/apis/user/login:user:otp.md): Second login step for users with two-factor authentication: verifies the 6-digit code from the authenticator app
for the session opened by POST /user/login.

Send the access token from the login response in the Authorization header. A valid code marks the session as having
passed two-factor authentication, after which the token works on all endpoints the user may use; the returned session has
an empty access_token: keep using the one from login. An invalid code answers 400 with success: false: the session
stays unauthenticated and accepts another code until remainingAttempts reaches 0. The last allowed wrong code ends the
session (sessionEnded: true) and the user has to log in with the password again. The number of attempts per login is
set with TWO_FACTOR_MAX_LOGIN_ATTEMPTS (default 5). A session that already passed two-factor authentication (or never
needed it) is returned unchanged.

Permission: Requires the session token from POST /user/login (the route itself is public, so a session that has
not passed two-factor authentication yet is accepted). Rate limited to 10 requests per minute per IP address.

Errors: 400 for an invalid code; 403 when no (open) session token is sent; 429 when the rate limit is exceeded.

### Log out the current session

 - [GET /user/logout](https://docs.transportial.com/apis/user/logout:user.md): Ends the session of the access token used for this request: its endedAt is set and its live (socket) connections
are marked as disconnected. Neither the access token nor the refresh token can be used afterwards. The returned session
has an empty refresh_token.

Permission: Any authenticated user; no specific permission.

Errors: 403 when not authenticated.

### Accept the terms & conditions

 - [POST /user/accept-terms](https://docs.transportial.com/apis/user/post:user:acceptterms.md): Records the acceptance of the terms & conditions for the platform of the request, made by the authenticated user on
the platform's behalf (the platform accepts, not the individual user).

The platform stores the version, moment and accepting user; the user's own acceptance fields are stamped as an audit
trail and their mustAcceptTerms flag is cleared. The recorded version is the platform's current terms version when it
has one, otherwise the version sent, otherwise 1.0. A platform provisioned by a super administrator that was waiting
for this acceptance is activated (platformActivated: true).

Permission: Any authenticated user whose role level reaches the platform's terms-acceptance level (default 1000), or
a provisioned owner who still has mustAcceptTerms.

Errors: 403 when not authenticated or when your role level is too low to accept for the platform; 404 when the
session does not belong to a user.

### Record a register-page step

 - [POST /user/register/attempt](https://docs.transportial.com/apis/user/record:user:signupattempt.md): Records one step of an anonymous visit to the register page, so signups that are never finished can be counted
in the super-admin signup funnel. The browser picks a UUID per visit (attemptId) and sends it with every step and with the
registration (signupAttemptId on POST /user/register), which links the attempt to the platform it created.

The first event of an attempt creates it and stores its attribution (visitor id, UTM tags, entry, referrer host); later events
never change the attribution. START and SUBMIT keep their first moment; ERROR stores the latest moment and error key.
Events for an attempt that already created a platform are ignored. No personal data is stored: no e-mail, name, IP address or
user agent.

The answer is {"success": true} in every case, also when nothing was recorded (invalid attempt id or event).

Permission: Public, no authentication. At most 60 requests per minute per IP address are recorded; beyond that events are
dropped, still with {"success": true} (never 429).

### Link an existing chauffeur to a user

 - [POST /user/{id}/assign-chauffeur](https://docs.transportial.com/apis/user/assign:chauffeur:user.md): Links an existing chauffeur (by chauffeurId in the body) to the user in the path, so that user
logs in as that chauffeur (driver app, own trips and activities). The link is one-to-one: the chauffeur
must not have a user yet and the user must not be linked to another chauffeur on your platform.
Returns the user.

Permission: update:chauffeur

Errors: 400 when chauffeurId is missing or blank, the chauffeur is already linked to a user, or
the user is already linked to a chauffeur; 404 when the user or the chauffeur does not exist on your
platform.

### List open sessions of the current user

 - [GET /user/sessions](https://docs.transportial.com/apis/user/get:user:activesessions.md): Returns all sessions of the authenticated user that have not ended and not expired, oldest first, including the one
used for this request. Each session includes its IP address and device; refresh_token is always empty in this list.

Permission: Any authenticated user; no specific permission.

Errors: 403 when not authenticated; 404 when the session does not belong to a user (API-access session).

### Start two-factor setup

 - [POST /user/2fa/setup](https://docs.transportial.com/apis/user/setup:user:2fa.md): Starts setting up two-factor authentication (TOTP) for the authenticated user. When two-factor authentication is
not on yet, a new secret and 16 recovery codes are generated (replacing any earlier unfinished setup). Returns a QR code
of the secret to scan with an authenticator app, the secret itself (secret, for typing or pasting it into an app on
the same phone) and the recovery codes. Two-factor authentication is only switched on
after a valid code is sent to POST /user/2fa/verify. When it is already on, the current secret's QR code is
returned with an empty recoveryCodes and no secret: both are only handed out when they are generated.

Permission: Any authenticated user; no specific permission.

Errors: 403 when not authenticated; 404 when the session does not belong to a user.

### Get the session of a completed SSO login

 - [GET /user/session](https://docs.transportial.com/apis/user/get:user:session:bylogintoken.md): Exchanges the token of a login attempt for the session it produced. Used by the web app after a SAML (single sign-on)
login: the identity provider redirects back to /auth/login/callback?token=... and the app calls this endpoint with that token.

The login attempt must belong to the platform of the request, must have succeeded and must not have expired more than
10 seconds ago (attempts live 90 seconds for SAML). When its session is still open and unexpired, a fresh access_token
is issued and the session returned. Otherwise the endpoint answers HTTP 200 with success: false and the message
session_expired (session ended or expired) or not_found (no matching successful login attempt).

Permission: Public, no authentication (the login-attempt token is the credential).

Errors: none besides success: false as described above.

### Register a user or a new platform

 - [POST /user/register](https://docs.transportial.com/apis/user/register:user.md): Public sign-up. What happens depends on the body:

- New platform (type user, accountType not supplier, and businessInformation.companyName or a legacy
billingId set): creates a platform with a business, an operational-base location and a billing on the primary
(free-tier) plan. businessInformation.nameSpace is required: it becomes the platform's nameSpace and its host name
{nameSpace}.{portal domain} (returned as portalHostName). An optional couponCode is redeemed on the new billing
(a discount and/or a free trial). The registrant becomes its administrator (role ADMIN) and only member. A ticked
termsAndConditions is recorded as the platform's acceptance. With an e-mail address a verification e-mail is sent
and the account stays DEFAULT until verified; without one the account is CONFIRMED immediately.
- Supplier access request (accountType supplier): creates the user and the business on the platform of the
request, pending approval by an administrator (POST /user/access-request/{id}/approve).
- User access request (type user otherwise): creates the user on the platform of the request, pending approval.

Access requests get role USER (level 0), are not members of the platform until approved, cannot log in before then
(unless they registered without an e-mail address), and trigger a confirmation e-mail to the applicant and a
notification to the platform's contact address. Default settings are created from the platform's primary settings group,
and the password is stored hashed. accountType is stored as sent.

Validation failures answer HTTP 200 with success: false: username or e-mail already used by any user
(user_username_email_exists), password and repeatPassword differ (passwords_no_match), the namespace of a new
platform is invalid, reserved or taken (platform_namespace_invalid, platform_namespace_reserved, platform_namespace_taken),
the reCAPTCHA check failed, or the couponCode of a new platform does not exist, is no longer valid, has expired or reached
its redemption limit (coupon_code_invalid, coupon_code_expired, coupon_code_exhausted; with params.field = couponCode).

Permission: Public, no authentication. Rate limited to 10 requests per minute per IP address.

Errors: 404 when the platform of the request cannot be determined for an access request; 429 when the rate limit is exceeded.

### Block a user

 - [POST /user/block/{id}](https://docs.transportial.com/apis/user/block:user.md): Blocks a user: sets the user's locked flag and ends all of the user's open sessions, so every device is signed
out immediately; POST /user/login refuses blocked users until they are unblocked. Only users whose primary platform is the platform of the request can be addressed.

Permission: block:user

Errors: 401 without the permission; 403 when not authenticated; 404 when no user with this id has your platform as primary platform.

### Change the password of the current user

 - [PUT /user/password](https://docs.transportial.com/apis/user/update:user:password.md): Changes the password of the authenticated user after checking the current password. Other sessions stay open.

When newPassword and repeatPassword differ, or currentPassword is wrong, the endpoint answers HTTP 200 with an
error body whose message is passwords_no_match. Note that this error body has success: true; check message/code.

Permission: Any authenticated user; no specific permission.

Errors: 403 when not authenticated; 404 when the session does not belong to a user.

## Team

A team groups users of a platform as members and managers, for example a planning department or branch. Transport orders, consignments and invoices can belong to a team: its prefixes shape generated order and consignment numbers, its managers must approve its invoices and are notified when its orders need replanning. Teams can claim orders saved without a team through prioritised, first-match assignment rules that filter the serialised order. This tag also covers settings groups (perspectives): bundles of default settings, hidden fields, a landing route and an optional keyboard shortcut for a set of users and teams; applying a group copies its default settings onto every member.

### List settings groups

 - [GET /settingsGroups/{offset}/{amount}](https://docs.transportial.com/apis/team/get:settingsgroups.md): Returns a page of the settings groups of the current platform (archived and deleted groups excluded), together with the total count. Each group includes its direct members and default settings.

Permission: get:settingsGroups

Errors: 400 when offset or amount is not an integer.

### Search settings groups

 - [POST /settingsGroups/{offset}/{amount}](https://docs.transportial.com/apis/team/search:settingsgroups.md): Full-text search over the settings groups of the current platform, matching the group name and the names of its direct members. Archived and deleted groups are excluded; results are sorted newest first. An empty query returns every group.

Permission: get:settingsGroups

Errors: 400 when the body is not valid JSON or offset/amount is not an integer.

### List teams

 - [GET /teams/{offset}/{amount}](https://docs.transportial.com/apis/team/get:teams.md): Lists the teams of your platform, most recently updated first, with their members. Deleted and archived teams are not returned.

Permission: get:teams

Errors: 400 when offset is not an integer.

### Search teams

 - [POST /teams/{offset}/{amount}](https://docs.transportial.com/apis/team/search:teams.md): Searches the teams of your platform in the search index by the free-text query (team name and members), newest first. Deleted and archived teams are never returned. A failing search is logged and returns an empty page.

Permission: get:teams

Errors: 400 when the body is not a JSON object or offset is not an integer.

### Update a settings group

 - [PUT /settingsGroup](https://docs.transportial.com/apis/team/update:settingsgroup.md): Updates the settings group identified by the id in the body; the sent fields are merged into the stored group. The users list replaces the direct members (references by id, unknown ids ignored): omitting it removes all direct members. Setting primary clears the flag on the platform's other groups, and claiming a shortcut removes it from any other group. When the id is unknown on every platform, a new group is created.

Permission: update:settingsGroup

Errors: 400 when the body is not valid JSON; 404 when the id belongs to a settings group of another platform.

### Create a settings group

 - [POST /settingsGroup](https://docs.transportial.com/apis/team/create:settingsgroup.md): Creates a settings group on the current platform. Direct members are sent in users as references by id; unknown ids are ignored. The base slug and shortcut are normalised (see the field descriptions); claiming a shortcut removes it from any other group of the platform. Teams and the primary flag cannot be set on create; use the update endpoint.

Permission: create:settingsGroup

Errors: 400 when the body is not valid JSON.

### Get a settings group

 - [GET /settingsGroup/{id}](https://docs.transportial.com/apis/team/get:settingsgroup.md): Returns one settings group of the current platform, including its direct members, hidden fields, local-storage settings and default settings.

Permission: get:settingsGroup

Errors: 404 when no settings group with this id exists on your platform.

### Delete a settings group

 - [DELETE /settingsGroup/{id}](https://docs.transportial.com/apis/team/delete:settingsgroup.md): Permanently deletes a settings group of the current platform. The members' personal settings are not changed. Returns the group as it was before deletion.

Permission: delete:settingsGroup

Errors: 404 when no settings group with this id exists on your platform.

### Update a team

 - [PUT /team](https://docs.transportial.com/apis/team/update:team.md): Updates the team identified by the id in the body. The update replaces the team's values: a field that is left out is cleared (empty name or prefixes, no members, no managers, no e-mail integration, priority 0, no assignment rules), so always send the complete team.

Fields read from the body: name, transportOrderPrefix, consignmentPrefix, assignmentPriority (default 0), orderAssignmentRules (a malformed list is stored as empty), users and managers (lists of { "id": "..." } references to users of your platform; unknown ids are skipped) and emailIntegration ({ "id": "..." } or the id as a string). vehicleGroups, fleets and features are ignored.

When no team with this id exists on your platform the request is not rejected; the values are applied to a new, unsaved team object instead.

Permission: update:team

Errors: 400 when the body is not a JSON object or has no id.

### Create a team

 - [POST /team](https://docs.transportial.com/apis/team/create:team.md): Creates a team on your platform.

Fields read from the body: name, transportOrderPrefix, consignmentPrefix, assignmentPriority (default 0), orderAssignmentRules (a malformed list is stored as empty), users and managers (lists of { "id": "..." } references to users of your platform; unknown ids are skipped) and emailIntegration ({ "id": "..." } or the id as a string). vehicleGroups, fleets and features are ignored.

Permission: create:team

Errors: 400 when the body is not a JSON object.

### Get a team

 - [GET /team/{id}](https://docs.transportial.com/apis/team/get:team.md): Returns one team of your platform with its members, vehicle groups, fleets and assignment rules.

Permission: get:team

Errors: 404 when no team with this id exists on your platform.

### Delete a team

 - [DELETE /team/{id}](https://docs.transportial.com/apis/team/delete:team.md): Permanently removes a team of your platform (hard delete) and returns it as it was. A failure while removing it is logged and the team is still returned with success: true.

Permission: delete:team

Errors: 404 when no team with this id exists on your platform.

### Apply a settings group to its members

 - [GET /settingsGroup/{id}/apply](https://docs.transportial.com/apis/team/apply:settingsgroup.md): Copies the group's default settings onto the personal settings of every member: the direct members and the users of its teams, each user once. Ids, timestamps and platform ownership are not copied. Increases the group's applyVersionNumber and sets lastSettingsApplied, which makes the clients re-apply the group's local-storage settings. A member whose settings cannot be updated is skipped.

Permission: apply:settingsGroup

Errors: 404 when no settings group with this id exists on your platform.

## Certificate

A certificate is a diploma, licence, insurance or other qualification held by a chauffeur, vehicle, transport equipment or business (a carrier's own certificate such as its CMR insurance): name, school, title, field of study, grade, type (`certificateType`, a code from the platform's carrier compliance settings), validity dates, attached documents, identifiers and notes. A holder is required on create and is returned as `holder`. Certificates are soft-deleted and can be listed, searched or filtered per chauffeur, business, vehicle or transport equipment. The certificate expiry check raises a warning alert per certificate expiring within the platform's look-ahead window (default 30 days, or the type's own warn days). With the `carrierCompliance` feature, a missing or expired certificate of a required type makes its holder unavailable for planning, forwarding and tenders.

### List certificates of a chauffeur

 - [GET /certificates/chauffeur/{id}/{offset}/{amount}](https://docs.transportial.com/apis/certificate/get:certificates:chauffeur.md): Returns a page of the certificates of the given chauffeur, most recently updated first. Archived and deleted certificates are left out; an unknown id gives an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:filteredCertificatesByChauffeur

Errors: 400 when offset is not a number.

### List certificates

 - [GET /certificates/{offset}/{amount}](https://docs.transportial.com/apis/certificate/get:certificates.md): Returns a page of the certificates of your platform, most recently updated first. Archived and deleted certificates are left out.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:Certificates

Errors: 400 when offset is not a number.

### Search certificates

 - [POST /certificates/{offset}/{amount}](https://docs.transportial.com/apis/certificate/search:certificates.md): Searches the certificates of your platform in the search index. Optional filters, combined with AND: query (phrase match with slop 10 on name, title, grade, school, the chauffeur, notes and documents) and chauffeurId. Results are sorted by sortBy when given, otherwise by relevance. Archived and deleted certificates are left out. A search the index cannot run (for example a sort on a non-sortable field) returns an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:Certificates

Errors: 400 when the body is not a JSON object or offset is not a number.

### Raise alerts for expiring certificates

 - [POST /certificates/expiring:check](https://docs.transportial.com/apis/certificate/check:certificates:expiring.md): Runs the certificate expiry check for your platform now: every non-deleted certificate (driver qualification such as Code 95, ADR, medical or licence, a vehicle or transport equipment certificate such as ATP, or a carrier's own certificate such as its CMR insurance) whose validUntil falls between now and daysAhead days from now gets a warning alert "Certificate expiring soon" in the alert inbox, linked to the chauffeur, vehicle, transport equipment or business that holds it. A certificate of a type configured in complianceSettings.carrierComplianceSettings.certificateTypes with its own warnDays uses those days instead of daysAhead.

A certificate that already has an unsolved expiry alert is skipped, so the check can be run repeatedly. Returns the window used and the number of new alerts. A failing check returns 0 alerts.

Permission: update:Certificate or create:Certificate (the check writes alerts)

Errors: 400 when the body is not empty and not a JSON object.

### Get a certificate

 - [GET /certificate/{id}](https://docs.transportial.com/apis/certificate/get:certificate.md): Returns one certificate of your platform by id, with its chauffeur, documents, identifiers and notes. Archived and deleted certificates are not found.

Permission: get:Certificate

Errors: 404 when no certificate with this id exists on your platform.

### Delete a certificate

 - [DELETE /certificate/{id}](https://docs.transportial.com/apis/certificate/delete:certificate.md): Soft-deletes a certificate: it is marked deleted and no longer returned by the certificate endpoints. Returns the deleted certificate.

Permission: delete:Certificate

Errors: 404 when no certificate with this id exists on your platform.

### List certificates of a vehicle

 - [GET /certificates/vehicle/{id}/{offset}/{amount}](https://docs.transportial.com/apis/certificate/get:certificates:vehicle.md): Returns a page of the certificates held by the given vehicle (for example ATP, tachograph calibration or inspection certificates), most recently updated first. Archived and deleted certificates are left out; an unknown id gives an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:Certificates or get:filteredCertificatesByBusiness

Errors: 400 when offset is not a number.

### List certificates of a business

 - [GET /certificates/business/{id}/{offset}/{amount}](https://docs.transportial.com/apis/certificate/get:certificates:business.md): Returns a page of the certificates linked to the given business, most recently updated first. Archived and deleted certificates are left out; an unknown id gives an empty page. The business link can only be set through the update endpoint.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:filteredCertificatesByBusiness

Errors: 400 when offset is not a number.

### Update a certificate

 - [PUT /certificate](https://docs.transportial.com/apis/certificate/update:certificate.md): Updates the certificate identified by the id in the body. Fields present in the body overwrite the stored values; fields left out keep their current value. When the id is not known on any platform, the body is stored as a new certificate. The holder links chauffeur, vehicle, transportEquipment and business are sent as { "id": "..." } of your platform; an absent link keeps the stored one and null clears it. The response returns the holder as holder.

Permission: update:Certificate

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a certificate of another platform or a holder id is not found on your platform.

### Create a certificate

 - [POST /certificate](https://docs.transportial.com/apis/certificate/create:certificate.md): Creates a certificate: a diploma, licence, insurance or other qualification with its name, school, title, field of study, grade, type (certificateType), validity period (validFrom/validUntil), attached documents, external identifiers and notes. A holder is required: chauffeur, vehicle, transportEquipment or business (a carrier's own certificate, such as its CMR insurance), each as { "id": "..." } of your platform. The response returns the holder as holder.

Permission: create:Certificate

Errors: 400 when the body is not valid JSON, does not match the certificate model or names no holder (certificate_holder_required); 404 when a holder id is not found on your platform.

### List certificates of transport equipment

 - [GET /certificates/transportEquipment/{id}/{offset}/{amount}](https://docs.transportial.com/apis/certificate/get:certificates:transportequipment.md): Returns a page of the certificates held by the given transport equipment (for example ATP, tachograph calibration or inspection certificates), most recently updated first. Archived and deleted certificates are left out; an unknown id gives an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:Certificates or get:filteredCertificatesByBusiness

Errors: 400 when offset is not a number.

## Ban

A ban is a named record with a free-text reason that links the chauffeur, business and/or actor it concerns, with external identifiers and notes. Bans are platform-scoped, can be created, updated (upsert by id) and soft-deleted, and can be listed, searched in the index or filtered per chauffeur, business or actor.

### List bans of a business

 - [GET /bans/business/{id}/{offset}/{amount}](https://docs.transportial.com/apis/ban/get:bans:business.md): Returns a page of the bans of your platform linked to the given business, most recently updated first. Archived and deleted bans are left out; an unknown id gives an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:BansFilteredByActor (the same permission guards the chauffeur, business and actor filters)

Errors: 400 when offset is not a number.

### Get a ban

 - [GET /ban/{id}](https://docs.transportial.com/apis/ban/get:ban.md): Returns one ban of your platform by id, with its linked chauffeur, business, actor, identifiers and notes. Archived and deleted bans are not found.

Permission: get:Ban

Errors: 404 when no ban with this id exists on your platform.

### Delete a ban

 - [DELETE /ban/{id}](https://docs.transportial.com/apis/ban/delete:ban.md): Soft-deletes a ban: it is marked deleted and no longer returned by the ban endpoints. Returns the deleted ban.

Permission: delete:Ban

Errors: 404 when no ban with this id exists on your platform.

### List bans

 - [GET /bans/{offset}/{amount}](https://docs.transportial.com/apis/ban/get:bans.md): Returns a page of the bans of your platform, most recently updated first. Archived and deleted bans are left out.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:Bans

Errors: 400 when offset is not a number.

### Search bans

 - [POST /bans/{offset}/{amount}](https://docs.transportial.com/apis/ban/search:bans.md): Searches the bans of your platform in the search index. Optional filters, combined with AND: query (phrase match with slop 10 on id, name, description, notes and the linked chauffeur, business and actor), chauffeurId, businessId, actorId and noteId (ban notes are not indexed as fields, so noteId currently yields an empty page). Results are sorted by sortBy when given, otherwise by relevance. Archived and deleted bans are left out. A search the index cannot run (for example a sort on a non-sortable field) returns an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:Bans

Errors: 400 when the body is not a JSON object or offset is not a number.

### List bans of a chauffeur

 - [GET /bans/chauffeur/{id}/{offset}/{amount}](https://docs.transportial.com/apis/ban/get:bans:chauffeur.md): Returns a page of the bans of your platform linked to the given chauffeur, most recently updated first. Archived and deleted bans are left out; an unknown id gives an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:BansFilteredByActor (the same permission guards the chauffeur, business and actor filters)

Errors: 400 when offset is not a number.

### List bans of a actor

 - [GET /bans/actor/{id}/{offset}/{amount}](https://docs.transportial.com/apis/ban/get:bans:actor.md): Returns a page of the bans of your platform linked to the given actor, most recently updated first. Archived and deleted bans are left out; an unknown id gives an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:BansFilteredByActor (the same permission guards the chauffeur, business and actor filters)

Errors: 400 when offset is not a number.

### Update a ban

 - [PUT /ban](https://docs.transportial.com/apis/ban/update:ban.md): Updates the ban identified by the id in the body. Fields present in the body overwrite the stored values; fields left out keep their current value. When the id is not known on any platform, the body is stored as a new ban.

Permission: update:Ban

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a ban of another platform.

### Create a ban

 - [POST /ban](https://docs.transportial.com/apis/ban/create:ban.md): Creates a ban on your platform: a named record with a free-text description that is linked to the chauffeur, business and/or actor it concerns, with optional external identifiers and notes. All links are optional. Returns the stored ban.

Permission: create:ban

Errors: 400 when the body is not valid JSON or does not match the ban model.

## Business

A company in your platform's address book: a customer, carrier, supplier or other relation, with its registration numbers (CoC, VAT, EORI, SCAC, transport licence), industry, logo, addresses, contact details, contact persons, external identifiers, documents and notes. Customers, transport orders, trips, quotes and invoices reference businesses. Name, CoC, VAT, EORI and identifiers are used to match an existing business instead of creating a duplicate. Businesses can be marked verified, are searched full-text, and are soft-deleted. Customer-portal users only see the businesses linked to their account.

### Unverify a business

 - [GET /business/{id}/unverify](https://docs.transportial.com/apis/business/unverify:business.md): Removes the verified mark from a business (verified = false). Calling it on a business that is not verified has no further effect.

Permission: unverify:business

Errors: 404 when no business with this id exists on your platform (deleted and archived businesses count as not found).

### List businesses

 - [GET /businesses/{offset}/{amount}](https://docs.transportial.com/apis/business/create:businesses.md): Returns a page of the non-deleted, non-archived businesses of your platform, most recently updated first, plus the total count in totalResults.

External users (for example customer-portal accounts) only see the businesses linked to their own account.

Permission: get:businesses

Errors: 400 when offset or amount is not an integer.

### Search businesses

 - [POST /businesses/{offset}/{amount}](https://docs.transportial.com/apis/business/create:businesss.md): Full-text search over the non-deleted, non-archived businesses of your platform. Returns one page of matches plus the total number of matches in totalResults.

Filters (all optional, combined with AND):
- query: matched against name, description, website, CoC, VAT and location data with prefix, phrase and fuzzy matching;
- verified: true returns only verified businesses;
- industries: industry names to match;
- relationType: text contained in the relation type.

Results are ordered by relevance unless sortBy is given. External users only see the businesses linked to their own account (none when no business is linked). A failing search (for example sorting on an unsortable field) returns an empty page instead of an error.

Permission: get:businesses

Errors: 400 when the body is not valid JSON or offset/amount is not an integer.

### Get a business

 - [GET /business/{id}](https://docs.transportial.com/apis/business/get:business.md): Returns a single business of your platform with its full details: registration numbers, industry, logo, locations, contact details, contact persons, identifiers, documents and notes.

Permission: get:business

Errors: 404 when no business with this id exists on your platform (deleted and archived businesses count as not found).

### Delete a business

 - [DELETE /business/{id}](https://docs.transportial.com/apis/business/delete:business.md): Soft-deletes a business: it is flagged as deleted and no longer returned by get, list or search endpoints. Entities that reference the business keep their reference.

Permission: delete:business

Errors: 404 when no business with this id exists on your platform (deleted and archived businesses count as not found).

### Get the credit position of a business

 - [GET /business/{id}/credit](https://docs.transportial.com/apis/business/get:businesscredit.md): Returns the credit position of a business as bill-to party: its credit status and limit, the open amount of its outgoing invoices (open, reminded or too late) and, when the platform counts them, the expected revenue of its accepted, planned and running orders without an invoice. The same figures are checked on order create and confirm. Also returns the platform's credit control rules and whether the caller may override a refusing check (override:creditLimit).

Permission: get:business

Errors: 404 when no business with this id exists on your platform.

### Verify a business

 - [GET /business/{id}/verify](https://docs.transportial.com/apis/business/verify:business.md): Marks a business as verified (verified = true), for example after its registration details have been checked. Verified businesses can be filtered on in search. Calling it on an already verified business has no further effect.

Permission: verify:business

Errors: 404 when no business with this id exists on your platform (deleted and archived businesses count as not found).

### Update a business

 - [PUT /business](https://docs.transportial.com/apis/business/update:business.md): Updates the business identified by the id in the body. Fields present in the body overwrite the stored values; collections sent replace the stored collections (contact persons left out are deleted).

industry and logo accept the id as a plain string or the { "id": "..." } object responses return; omitting them keeps the stored value and null clears them. verified is ignored (the stored value is kept) unless you have the verify:business permission. When no business with the body id exists on your platform (and the id is not used by another platform) a new business is created with that id. Saving queues the AfterUpdatingBusiness (or AfterCreatingBusiness) integration trigger and notifies users newly mentioned in notes.

Permission: update:business

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a business of another platform.

### Create a business

 - [POST /business](https://docs.transportial.com/apis/business/create:business.md): Creates a business on your platform.

industry and logo are read as plain id strings (an industry id and an uploaded file id). Each entry in locations is matched to an existing location of your platform or created and geocoded; entries that cannot be resolved are dropped. verified is only accepted from callers with the verify:business permission; for everyone else the business is created unverified. Saving queues the AfterCreatingBusiness integration trigger for listening integrations and notifies users mentioned in notes.

Permission: create:business

Errors: 400 when the body is not valid JSON or cannot be mapped to a business.

### Apply a bulk action to businesses

 - [POST /businesses/bulk](https://docs.transportial.com/apis/business/bulk:business.md): Applies one action to several businesses at once. The only supported action is delete, which soft-deletes every listed business (same effect as deleting them one by one). Ids that do not match a non-deleted, non-archived business of your platform are skipped. The response lists the ids that were updated.

Permission: delete:business

Errors: 400 when ids is empty, when action is not delete, or when the body is not valid JSON.

### Set the carrier status of a business

 - [PUT /business/{id}/carrierStatus](https://docs.transportial.com/apis/business/update:carrierstatus.md): Sets the standing of a business as carrier (supplier side): approved, warning or blocked, with a reason; null clears it (not assessed). The server records who set it and when. A blocked carrier is left out of recommendations and tenders, and forwarding to it is warned about or refused per complianceSettings.carrierComplianceSettings.forwardingPolicy. Separate from the customer-side credit status; the generic business create and update ignore these fields.

Permission: update:carrierStatus; the platform needs the carrierCompliance feature.

Errors: 400 with carrier_compliance_disabled without the feature, or when the body is not valid JSON or has an unknown status; 404 when no business with this id exists on your platform.

## Location

A location is an entry in the platform's location book: an address or site (customer, warehouse, terminal, parking spot, fuel station, ...) or a zone, with a geo reference (usually a lat/lon point), postal address, contacts, opening times, constraints, cost lines and an IANA time zone. Locations are referenced by actions (stops), orders, trips and vehicles, so deleting one only flags it as deleted. On create or update the address is geocoded with Google when the point is still 0,0, an address hash is kept for duplicate detection, and the time zone is derived from the coordinates unless set by hand. A location with a geofence radius also acts as a circular geofence for vehicle enter/exit alerts. Customer-portal users only see their own address book or anonymised locations, depending on the platform's privacy setting.

### List the consignments passing through a cross-dock hub

 - [GET /location/{id}/crossDock](https://docs.transportial.com/apis/location/get:location:crossdock.md): Returns every consignment that is unloaded at this location by one trip to be loaded onto another: still on its way in (INBOUND), waiting at the hub (AT_HUB) or already loaded onward (DEPARTED, kept for 2 days). Each row rates the connection against the location's transferTime: OK, TOO_TIGHT, NO_ONWARD_LEG or UNKNOWN.

Stops where the location is the consignment's own pick-up or delivery address are not transfers and are left out.

Permission: get:location

Errors: 404 when no live location with this id exists on your platform.

### List locations

 - [GET /locations/{offset}/{amount}](https://docs.transportial.com/apis/location/get:locations.md): Returns one page of the location book of your platform, most recently updated first (updatedAt descending, id as tiebreaker). Deleted and archived locations are left out. Use POST /locations/{offset}/{amount} to search or filter.

Permission: get:locations

Errors: 400 when offset is not an integer.

### Search locations

 - [POST /locations/{offset}/{amount}](https://docs.transportial.com/apis/location/search:locations.md): Full-text search over the location book of your platform (search index). Deleted and archived locations are never returned.

- query matches name and address with prefix, phrase, contains and fuzzy matching; best matches come first.
- types limits the result to the given location types, mustBeTerminal to locations linked to a terminal, and sortBy adds field sorts after relevance.
- Customer-portal users: under the visibility setting OWN_ONLY the search runs on their own address book only (by query); under ADDRESS_ONLY locations that are not their own come back anonymised (name, contacts and remarks removed, address kept).

A failing search is logged and returns an empty page.

Permission: get:locations

Errors: 400 when the body is not a JSON object or offset is not an integer.

### Update a location

 - [PUT /location](https://docs.transportial.com/apis/location/update:location.md): Updates the location identified by id in the body. The body is merged onto the stored location: fields that are left out keep their value.

- When the address (administrativeReference) changed and the submitted point is 0,0 or still on the old position (within 200 m), the new address is geocoded with the Google Geocoding API and replaces the point.
- Time zone: a changed, valid IANA timeZone becomes manual (timeZoneManual = true); a blank one clears the override so the zone is derived from the coordinates again; an invalid one is ignored; an explicit timeZoneManual wins. A location that moved re-derives a non-manual zone on save.
- The duplicate-detection hash is recalculated from the address.

Permission: update:location

Errors: 400 when the body is not valid JSON; 404 when id is missing or no live location with this id exists on your platform.

### Create a location

 - [POST /location](https://docs.transportial.com/apis/location/create:location.md): Creates a location (address, site or zone) in the location book of your platform.

- When geoReference is a latLonPointGeoReference at 0,0 (not geocoded yet), the address in administrativeReference is geocoded with the Google Geocoding API (platform key first, instance key as fallback). If geocoding finds nothing the point stays at 0,0.
- timeZone: a valid IANA zone sent here is stored as a manual zone (timeZoneManual = true); without one the zone is derived from the coordinates on save.
- A blank administrativeReference.name is filled with the location name, and the duplicate-detection hash is recalculated from the address.
- A customer-portal user creating a location gets it linked to their own address book, so it shows up in their autocomplete.

Permission: create:location

Errors: 400 when the body is not valid JSON or contains an unknown type/enum value.

### Get the average action duration at a location

 - [GET /location/{id}/action/{type}/duration{average}](https://docs.transportial.com/apis/location/location:action:duration:average.md): Returns how long actions of one type take at this location on average, from the realized actions of that type that have both a start and an end time (endTime - startTime). Used to predict handling time when planning a new stop here. Clients call it as /location/{id}/action/{type}/duration:average.

An unknown action type, or a location without realized actions of that type, returns duration = 0 and count = 0.

Permission: location:action:duration:average

Errors: 404 when no live location with this id exists on your platform.

### Get a location

 - [GET /location/{id}](https://docs.transportial.com/apis/location/get:location.md): Returns one location of your platform with its address, geo reference, contact details, opening times, terminal, constraints, pricing elements and identifiers.

Customer-portal users: under the visibility setting OWN_ONLY only locations in their own address book can be read (others answer 404); under ADDRESS_ONLY a location that is not their own comes back anonymised (name, contacts, remarks, identifiers, opening times and constraints removed, address kept).

Permission: get:location

Errors: 404 when no live (not deleted, not archived) location with this id exists on your platform, or a portal user may not see it.

### Delete a location

 - [DELETE /location/{id}](https://docs.transportial.com/apis/location/delete:location.md): Soft-deletes a location: it is flagged deleted and disappears from lists, searches and reads, but the row stays so actions, trips and orders that refer to it keep their address.

Permission: delete:location

Errors: 404 when no live location with this id exists on your platform, or the session has no user.

### List stops at a location

 - [GET /location/{id}/actions/{offset}/{amount}](https://docs.transportial.com/apis/location/get:location:actions.md): Returns one page of the stops (actions of type stop) planned or executed at this location, most recently updated first (updatedAt descending, id as tiebreaker). Use it to see the visit history of a site.

Optional query filters: lifeCycle (only stops in that life cycle) and from/to (bounds on the stop's updatedAt, inclusive).

Permission: location:action:duration:average

Errors: 400 when offset is not an integer; 404 when no live location with this id exists on your platform.

### Find an existing location with the same address

 - [POST /location/duplicates](https://docs.transportial.com/apis/location/get:location:duplicates.md): Checks whether the location book of your platform already holds a location with the same address before you create one. The address hash is computed from street, house number, addition, address line 2, city, state, postal code, country and UN/LOCODE (lower-cased, letters and digits only) and compared with the stored hashes. Nothing is saved.

Returns at most one live (not deleted, not archived) match; an empty list means no duplicate.

Permission: get:location:duplicates

Errors: 400 when the body is not valid JSON.

### List geofence locations

 - [GET /locations/geofences/{offset}/{amount}](https://docs.transportial.com/apis/location/get:locations:geofences.md): Returns one page of the locations of your platform that act as a circular geofence (a geofenceRadiusMeters is set and the geo reference is a point), as compact rows ordered by name. Deleted locations are left out. Vehicle positions are checked against these geofences for enter/exit alerts.

Permission: get:locations

Errors: none specific; an invalid offset falls back to 0 and amount is clamped to 1..100.

### Search geofence locations by name

 - [POST /locations/geofences/{offset}/{amount}](https://docs.transportial.com/apis/location/search:locations:geofences.md): Same as GET /locations/geofences/{offset}/{amount}, narrowed to geofence locations whose name contains query (case-insensitive). An empty, missing or unparsable body returns every geofence.

Permission: get:locations

Errors: none specific; an invalid offset falls back to 0 and amount is clamped to 1..100.

### Validate that an address exists

 - [POST /location/geocode/validateAddressGeoReference](https://docs.transportial.com/apis/location/geocode:addressgeoreference:validate.md): Checks whether an address resolves to an existing place with the Google Geocoding API (platform key first, instance key as fallback). Unlike the plain geocode endpoint it inspects the geocoder status, so "not found" is distinguishable from a real match. Nothing is saved.

valid is true when the geocoder answered OK with at least one result; partialMatch is true when Google only matched part of the address; formattedAddress is Google's formatted version of the first result. A geocoder failure returns valid = false with a 0,0 point.

Permission: geoCode:addressGeoReference

Errors: 400 when the body is not valid JSON.

### Geocode an address to coordinates

 - [POST /location/geocode/addressGeoReference](https://docs.transportial.com/apis/location/geocode:addressgeoreference.md): Turns an address into a latLonPointGeoReference with the Google Geocoding API, using the platform's Google API key or the instance key as fallback. The query follows the country's own address order; a two-letter country restricts the result to that country (UK is treated as GB); addressLine2 is not sent. The first result is used. Nothing is saved.

When the address cannot be found or the geocoder fails, the point is lat = 0, lon = 0; use POST /location/geocode/validateAddressGeoReference to tell "not found" apart.

Permission: geoCode:addressGeoReference

Errors: 400 when the body is not valid JSON.

## Contract

Contracts between a contractor and a client business: Incoterms delivery terms, a validity period, signees for both sides, an administration and the pricing (agreed rates) that applies. Contract numbers (C00001, ...) are issued per platform when left blank. List and search return lean rows without description, signees, pricing and identifiers. Update replaces the whole contract, and delete is permanent.

### Update a contract

 - [PUT /contract](https://docs.transportial.com/apis/contract/update:contract.md): Replaces the contract identified by id in the body. This is a full replacement, not a merge: title, description, references, validity and identifiers that are left out are cleared (identifiers are replaced as a whole). A blank contractNr keeps the current number. Fetch the full contract by id first; list rows leave fields empty and would clear them when sent back.

Every field is read individually from the body:
- contractor, client, contractorSignee, clientSignee, pricing and administration may be an object with an id or a bare id string; they are resolved on your platform and an unknown id leaves the field empty.
- deliveryTerms is matched case-insensitively; unknown values keep the current terms.
- validFrom/validUntil are ISO 8601 instants; unparseable values are stored as empty.
- language is a language id; when it is missing or unknown the platform default language is used.

Permission: update:contract

Errors: 400 when the body is not valid JSON or has no id; 404 when no contract with this id exists on your platform.

### Create a contract

 - [POST /contract](https://docs.transportial.com/apis/contract/create:contract.md): Creates a contract on your platform. When contractNr is blank the next contract number is issued (C + last number + 1, zero-padded to 5 digits).

Every field is read individually from the body:
- contractor, client, contractorSignee, clientSignee, pricing and administration may be an object with an id or a bare id string; they are resolved on your platform and an unknown id leaves the field empty.
- deliveryTerms is matched case-insensitively; unknown values keep the current terms.
- validFrom/validUntil are ISO 8601 instants; unparseable values are stored as empty.
- language is a language id; when it is missing or unknown the platform default language is used.

Permission: create:contract

Errors: 400 when the body is not valid JSON.

### Get a contract by title

 - [GET /contract/title/{title}](https://docs.transportial.com/apis/contract/get:contract:by-title.md): Returns the most recently created contract of your platform whose title equals the given title exactly (case-sensitive). Deleted and archived contracts are not excluded by this lookup.

Permission: get:contract

Errors: 404 when no contract with this title exists on your platform.

### List contracts

 - [GET /contracts/{offset}/{amount}](https://docs.transportial.com/apis/contract/get:contracts.md): Returns one page of the contracts of your platform, most recently updated first. Deleted and archived contracts are not included. Rows are lean: description, signees, pricing and identifiers are left empty.

Permission: get:contracts

Errors: 400 when offset is not an integer.

### Search contracts

 - [POST /contracts/{offset}/{amount}](https://docs.transportial.com/apis/contract/search:contracts.md): Searches the contracts of your platform. Deleted and archived contracts are never returned. Rows are lean: description, signees, pricing and identifiers are left empty.

- A body with only clientId (and optionally query) runs as a direct database lookup of that client's contracts, newest first, with query matching contract number or title by substring.
- Any other body uses the search index with the free-text query and the facets customers, contractors, deliveryTerms, validity, validFrom, validUntil, createdAt and hasPricing. Without sortBy, results are ordered by relevance when there is a query, otherwise newest first.

A failing search is logged and returns an empty page.

Permission: search:contracts

Errors: 400 when the body is not a JSON object or offset is not an integer.

### Get a contract

 - [GET /contract/{id}](https://docs.transportial.com/apis/contract/get:contract.md): Returns one contract of your platform by id with all its fields: description, signees, pricing and identifiers.

Permission: get:contract

Errors: 404 when no contract with this id exists on your platform.

### Delete a contract

 - [DELETE /contract/{id}](https://docs.transportial.com/apis/contract/delete:contract.md): Deletes the contract permanently (hard delete) and returns it as it was before deletion.

Permission: delete:contract

Errors: 404 when no contract with this id exists on your platform.

## Terminal

A container terminal (port, rail or barge) that trucks visit to pick up or deliver containers. Terminals are imported from Portbase (matched on name) or created manually, carry codes (EAN, BICS, SMDG), accepted transport modes and per-modality rules (TAR, seals, reefers, dangerous goods, cargo cut-off), and are linked to address-book locations. Confirming a trip with actions at a Portbase terminal pre-notifies the visits to Portbase; cancelling the trip cancels them.

### Get a terminal

 - [GET /terminal/{id}](https://docs.transportial.com/apis/terminal/get:terminal.md): Returns a single terminal of your platform with its locations and terminal settings.

Permission: get:terminal (and a permission level of at least 300)

Errors: 404 when no terminal with this id exists on your platform.

### Delete a terminal

 - [DELETE /terminal/{id}](https://docs.transportial.com/apis/terminal/delete:terminal.md): Soft-deletes a terminal: it is flagged as deleted and no longer listed or searchable. Returns the deleted terminal. Only users with a role level of at least 1000 (administrators) can delete; others get a not-authorized response.

Permission: delete:terminal (and a permission level of at least 300)

Errors: 404 when no terminal with this id exists on your platform.

### List terminals

 - [GET /terminals/{offset}/{amount}](https://docs.transportial.com/apis/terminal/get:terminals.md): Returns one page of the terminals of your platform, most recently updated first, with the total number of terminals. Deleted and archived terminals are excluded.

Permission: get:terminals (and a permission level of at least 300)

Errors: 400 when offset is not an integer.

### Search terminals

 - [POST /terminals/{offset}/{amount}](https://docs.transportial.com/apis/terminal/search:terminals.md): Full-text search over the terminals of your platform. query is matched against the name, EAN, BICS code, quay name, SMDG code and the locations of each terminal; an empty query returns all terminals. Results are ordered newest first and paged with offset and amount. A failing search returns an empty page.

Permission: search:terminals (and a permission level of at least 300)

Errors: 400 when the body is not valid JSON or offset is not an integer.

### Update a terminal

 - [PUT /terminal](https://docs.transportial.com/apis/terminal/update:terminal.md): Updates the terminal identified by the id in the body. Fields present in the body overwrite the stored values; omitted fields are kept. Locations sent with the terminal are linked to it. When no terminal with this id exists yet, a new one is created with it.

Permission: update:terminal (and a permission level of at least 300)

Errors: 400 when the body is not valid JSON or fails validation; 404 when the id belongs to a terminal of another platform.

### Create a terminal

 - [POST /terminal](https://docs.transportial.com/apis/terminal/create:terminal.md): Creates a terminal on your platform and returns it. Terminals are usually imported by the Portbase integration; create one manually for terminals outside a port community system. Link address-book locations to it through the location's terminal.

Permission: create:terminal (and a permission level of at least 300)

Errors: 400 when the body is not valid JSON.

## Actor

An Actor is a party involved in transport (planner, carrier, shipper, consignee, consignor or receiver) that is not necessarily a platform user. It has a name, optionally the user it represents, external identifiers, and contact details sent as an OTM5 association list. Actors are linked to vehicles and routes. Deleting an actor marks it as deleted.

### Get an actor

 - [GET /actor/{id}](https://docs.transportial.com/apis/actor/get:actor.md): Returns one actor of your platform with its identifiers and contact details.

Permission: get:actor

Errors: 404 when no actor with this id exists on your platform (deleted and archived actors are not found).

### Delete an actor

 - [DELETE /actor/{id}](https://docs.transportial.com/apis/actor/delete:actor.md): Marks the actor as deleted. It no longer appears in GET, lists or searches. Returns the deleted actor.

Permission: delete:actor

Errors: 404 when no actor with this id exists on your platform, or the request is not made by a user session.

### Update an actor

 - [PUT /actor](https://docs.transportial.com/apis/actor/update:actor.md): Merges the request body into the stored actor identified by the body id; fields left out keep their stored value. When no actor with that id exists yet, it is created with that id.

Permission: update:actor

Errors: 400 when the body is not valid JSON; 404 when the id belongs to an actor of another platform.

### Create an actor

 - [POST /actor](https://docs.transportial.com/apis/actor/create:actor.md): Creates an actor with its identifiers and contact details. user is resolved by its id on your platform; when the id is unknown there, the actor is created without a user.

Permission: create:actor

Errors: 400 when the body is not valid JSON.

### List actors

 - [GET /actors/{offset}/{amount}](https://docs.transportial.com/apis/actor/get:actors.md): Returns one page of the actors of your platform, most recently updated first. Deleted and archived actors are left out.

Permission: get:actors

Errors: 400 when offset is not an integer.

### Search actors

 - [POST /actors/{offset}/{amount}](https://docs.transportial.com/apis/actor/search:actors.md): Full-text search over the actors of your platform, newest first. query is matched against the actor's search text, as a phrase or, when it contains * or |, with simple query syntax; an empty query returns every actor. Deleted and archived actors are left out.

Permission: search:actors

Errors: 400 when the body is missing or not valid JSON, or offset is not an integer.

## Attribute

Attributes are platform-defined labels with a name and a display colour, attached to vehicles, locations and actions to categorise and filter them. When an entity references an attribute without a known id, the server links the attribute with the same name on the platform or creates it. Attributes can be managed one by one, or replaced as a whole list with PUT /attributes. Deleting an attribute marks it as deleted.

### List attributes

 - [GET /attributes/{offset}/{amount}](https://docs.transportial.com/apis/attribute/get:attributes.md): Returns one page of the attributes of your platform, most recently updated first. Deleted and archived attributes are left out.

Permission: get:attributes

Errors: 400 when offset is not an integer.

### Search attributes

 - [POST /attributes/{offset}/{amount}](https://docs.transportial.com/apis/attribute/search:attributes.md): Full-text search over the attributes of your platform, most recently updated first. query is matched as a phrase (slop 10) against the attribute's name and colour; a blank query returns every attribute. Deleted and archived attributes are left out.

Permission: get:attributes

Errors: 400 when the body is missing or not valid JSON, or offset is not an integer.

### Get an attribute

 - [GET /attribute/{id}](https://docs.transportial.com/apis/attribute/get:attribute.md): Returns one attribute of your platform.

Permission: get:attribute

Errors: 404 when no attribute with this id exists on your platform (deleted and archived attributes are not found).

### Delete an attribute

 - [DELETE /attribute/{id}](https://docs.transportial.com/apis/attribute/delete:attribute.md): Marks the attribute as deleted. It no longer appears in GET, lists or searches. Returns the deleted attribute.

Permission: delete:attribute

Errors: 404 when no attribute with this id exists on your platform, or the request is not made by a user session.

### Update an attribute

 - [PUT /attribute](https://docs.transportial.com/apis/attribute/update:attribute.md): Merges the request body into the stored attribute identified by the body id; fields left out keep their stored value. When no attribute with that id exists yet, it is created with that id.

Permission: update:attribute

Errors: 400 when the body is not valid JSON; 404 when the id belongs to an attribute of another platform.

### Create an attribute

 - [POST /attribute](https://docs.transportial.com/apis/attribute/create:attribute.md): Creates an attribute (a label with a name and colour) on your platform. It can then be attached to vehicles, locations and actions.

Permission: create:attribute

Errors: 400 when the body is not valid JSON.

### Replace all attributes

 - [PUT /attributes](https://docs.transportial.com/apis/attribute/put:attributes.md): Replaces the attribute list of your platform with the request body. Every existing attribute is marked deleted, and every item in the body is stored as a new attribute with a new id: ids in the body are not read, so items never match existing attributes. Vehicles, locations and actions that were linked to the old attributes are not relinked to the new ones. Returns the stored attributes; totalResults is the number of items stored.

Permission: put:attributes

Errors: 400 when the body is not a JSON array.

## Type

Types are platform-defined options for the app's type fields: transport equipment type and sub-type, consignment type, fuel type, vehicle type and driver review category. A type has a label, the stored value, an optional parent type, default field values that forms apply when it is picked, and fields that forms hide for entities of that type. Entities store the type as its plain value. Listing and searching need an admin-level role.

### Update a type

 - [PUT /type](https://docs.transportial.com/apis/type/update:type.md): Replaces the editable fields of the type identified by the id in the body: targetEntityType, label, value, childOf, defaultValues and hiddenFields are all taken from the body, so a field left out is reset to its default (empty text, no parent, empty map or list, transportEquipmentType). nonce is copied from the body. Deposit settings are not changed by this endpoint.

Permission: update:type

Errors: 400 when the body is not valid JSON, has no id, or does not match the type model; 404 when no type with this id exists on your platform.

### Create a type

 - [POST /type](https://docs.transportial.com/apis/type/create:type.md): Creates a type: a platform-defined option for one of the type fields in the app, chosen by targetEntityType (transport equipment type or sub-type, consignment type, fuel type, vehicle type or driver review category). A type has a display label, the stored value, an optional parent (childOf, for sub-types), default field values that forms apply when the type is picked, and fields that forms hide for entities of this type. Returns the stored type.

Permission: create:type

Errors: 400 when the body is not valid JSON, does not match the type model, or has an unknown targetEntityType.

### List types

 - [GET /types/{offset}/{amount}](https://docs.transportial.com/apis/type/get:types.md): Returns a page of the types of your platform, most recently updated first. Archived and deleted types are left out.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: get:types. Besides the permission, the caller needs a fully authenticated session (two-factor authentication passed where enabled) and a role of level 50 or higher; otherwise the request is refused with 401.

Errors: 400 when offset is not a number.

### Search types

 - [POST /types/{offset}/{amount}](https://docs.transportial.com/apis/type/search:types.md): Searches the types of your platform in the search index: query is matched as a phrase (slop 10) against the target entity type, label and value, and results are sorted by sortBy when given, otherwise by relevance. Unlike the list endpoint, this search does not leave out deleted or archived types. A search the index cannot run returns an empty page.

Paging: offset and amount select the page; totalResults in the response is the total number of matching records, not the page size.

Permission: search:types. Besides the permission, the caller needs a fully authenticated session (two-factor authentication passed where enabled) and a role of level 50 or higher; otherwise the request is refused with 401.

Errors: 400 when the body is not a JSON object or offset is not a number.

### Get a type

 - [GET /type/{id}](https://docs.transportial.com/apis/type/get:type.md): Returns one type of your platform by id. Archived and deleted types are not found.

Permission: get:type

Errors: 404 when no type with this id exists on your platform.

### Delete a type

 - [DELETE /type/{id}](https://docs.transportial.com/apis/type/delete:type.md): Soft-deletes a type: it is marked deleted and no longer returned by the get and list endpoints. Entities that already store the type's value keep it. Returns the deleted type.

Permission: delete:type. The permission must also be granted directly on the caller's role; otherwise the request is refused with 401.

Errors: 404 when no type with this id exists on your platform.

## Quote

A price quote of an administration: outgoing (a price offer to a customer, numbered from the administration's quote sequence) or incoming (a carrier's or supplier's offer, also created by AI from uploaded documents or e-mail). It holds priced lines, some of which may be optional extras; totals and expected ledger entries are recalculated on every save. A quote starts as concept, becomes open when sent (with PDF via the quote-send message automation), and is accepted or declined by the recipient, also without logging in through its public share link. An accepted quote can be turned into a transport order, and any quote into an invoice.

### Approve an extra charge of a shared quote

 - [POST /quote:public/{id}/charge/{lineId}/approve/{shareCode}](https://docs.transportial.com/apis/quote/approve:quote:charge:public.md): Extra charges are quote lines added after the price was agreed that need approval. Only approved charges count towards the totals, the expected revenue (ledger) and invoices. Lets the recipient approve a charge waiting for them (to_approve) through the quote's public share link, without logging in. The body must carry consent: true, the observedContentHash and the name and e-mail address the platform requires; the approval is recorded in the agreement log and triggers afterApprovingCharge.

Permission: Public, no authentication; access is granted by the share code.

Errors: 400 charge_decision_not_allowed, charge_already_invoiced or a missing consent, name or e-mail; 409 quote_changed; 401 when sharing is closed, the share link has expired or the share code does not match; 404 when the quote or charge does not exist.

### Preview quote lines for a calculated route from a pricing

 - [POST /quote/lines/pricing/{pricingId}/route](https://docs.transportial.com/apis/quote/create:quotelinesusingroute.md): Calculates quote lines for a route that was already calculated (for example in the route planner), using the modules of a pricing; nothing is stored. Use it to quote a route straight from its calculation.

Unlike the order variant, the route is not calculated again: one virtual trip is made over the order's stops with the given route, so the distance, legs and tolls are exactly those of the calculation. Every pricing module prices the order and that trip, as when invoicing (trip status is ignored). Modules that fail are skipped. The lines are grouped by description, get sub-total, tax and total, and are numbered in that order; when nothing could be priced a single empty line is returned.

Permission: create:quoteLinesUsingOrder

Errors: 400 when the body is not valid JSON, or the order has fewer than two stops with a coordinate; 404 when no pricing with this id exists on your platform; 500 when the pricing has no modules.

### Create a transport order from a quote

 - [POST /quote/{id}/create:transportOrder](https://docs.transportial.com/apis/quote/create:quote:transportorder.md): Creates a transport order from an accepted quote and links the two.

The order gets the quote's name, description, administration and documents; the quote's recipient (targetBusiness) becomes the order's customer and its issuer (subjectBusiness) the executing business. When the quote is already linked to a transport order, that order is returned and nothing is created. When the quote is not accepted, nothing is created and transportOrder is null. No request body.

Permission: create:transportOrder

Errors: 404 when no quote with this id exists on your platform, or a customer-portal user may not see it (only quotes of their own businesses).

### Preview quote lines for an order from a pricing

 - [POST /quote/lines/pricing/{pricingId}](https://docs.transportial.com/apis/quote/create:quotelinesusingorder.md): Calculates quote lines for a transport order that does not need to exist yet, using the modules of a pricing; nothing is stored. Use it to pre-fill the lines of a quote while the order is being entered.

The order in the body is planned into virtual trips per transport mode (when it lacks the consignments, actions or locations needed for that, no trips are made) and every pricing module prices the order and those trips, as when invoicing (trip status is ignored). Modules that fail are skipped. The lines are grouped by description, get sub-total, tax and total, and are numbered in that order; when nothing could be priced a single empty line is returned.

Permission: create:quoteLinesUsingOrder

Errors: 400 when the body is not valid JSON or does not match the order model; 404 when no pricing with this id exists on your platform; 500 when the pricing has no modules.

### Decline an optional quote line

 - [POST /quote/{id}/option/{lineId}/decline](https://docs.transportial.com/apis/quote/decline:quote:option.md): Declines one optional line of a quote. A declined option stays on the quote but is left out of the totals, the ledger and the invoice. The totals and the expected ledger entries are recalculated, and a DECLINED activity is logged. Options can be decided while the quote is concept, open or accepted, also after the transport order was accepted. Once the quote is accepted, a decision is a new acceptance: it is recorded in the quote's agreement log with a sealed PDF of the result, and it is refused when the platform requires customer acceptance. A customer-portal user must send consent: true and the observedContentHash.

Permission: decline:quote

Errors: 400 when the quote is declined, cancelled or invoiced, or quote_customer_acceptance_required when only the customer may decide on an accepted quote; 409 quote_changed when an observedContentHash is sent that no longer matches; 404 when the quote does not exist or is not visible to you, or has no optional line with this id.

### Send an extra charge to the customer for approval

 - [POST /quote/{id}/charge/{lineId}/submit](https://docs.transportial.com/apis/quote/submit:quote:charge.md): Extra charges are quote lines added after the price was agreed that need approval. Only approved charges count towards the totals, the expected revenue (ledger) and invoices. Gives the internal approval of a charge (internal_approval_requested -> to_approve), or offers a rejected charge again. The customer then sees it in the portal and through the quote's share link; triggers afterRequestingChargeApproval so a configured message automation can notify the customer.

Permission: approve:charge

Errors: 400 charge_decision_not_allowed or charge_already_invoiced; 404 when the quote or charge does not exist or is not visible to you.

### Mark an extra charge as invoiced externally

 - [POST /quote/{id}/charge/{lineId}/invoiced-externally](https://docs.transportial.com/apis/quote/invoicedexternally:quote:charge.md): Extra charges are quote lines added after the price was agreed that need approval. Only approved charges count towards the totals, the expected revenue (ledger) and invoices. Marks a charge that was invoiced outside the platform (invoiced_externally). It is then left out of totals, revenue and invoices here. Not possible on a rejected charge or one already on an invoice.

Permission: approve:charge

Errors: 400 charge_decision_not_allowed or charge_already_invoiced; 404 when the quote or charge does not exist or is not visible to you.

### Receive a quote by e-mail

 - [PUT /quote/mail/receiver](https://docs.transportial.com/apis/quote/create:quote:frommail.md): Inbound e-mail webhook (SendGrid Inbound Parse format) that turns an e-mailed quote into an incoming (in) quote.

The platform is taken from the recipient address (@...transportial.com) and the sender's domain must be one of the platform's allowed e-mail domains. The attachments are stored as files and the request is acknowledged immediately; in the background the e-mail body and attachments are read with the platform's AI model and saved as an incoming quote of the primary administration (an existing matching quote is updated instead), so processing errors are not reported to the caller.

Permission: Public, no authentication.

Errors: 400 when the platform cannot be found, the sender address is missing or invalid, or the sender's domain is not allowed.

### List quotes of an administration

 - [GET /quotes/{administration}/{offset}/{amount}](https://docs.transportial.com/apis/quote/get:quotes.md): Returns the quotes (outgoing and incoming) of one administration, newest first, plus the total count. Deleted and archived quotes are excluded.

Customer accounts only get outgoing quotes addressed to their businesses, supplier accounts only the incoming quotes their businesses sent. An unknown administration id returns an empty list.

Permission: get:quotes

Errors: 400 when offset is not an integer.

### Search quotes

 - [POST /quotes/{administration}/{offset}/{amount}](https://docs.transportial.com/apis/quote/search:quotes.md): Searches the quotes of your platform in one direction (outgoing by default) with free text, status, date, due-date, counterparty, overdue and team filters, sorted by relevance or by the requested fields. Deleted and archived quotes are excluded.

When the administration path segment is the id of one of your administrations only its quotes are searched; an unknown id searches all administrations. Portal users only see their own quotes, whatever direction they send: customer accounts our outgoing quotes addressed to their businesses, supplier (carrier) accounts the incoming quotes their businesses sent us; they get an empty page when they have no businesses. A failing search returns an empty page.

Permission: search:quotes

Errors: 400 when offset is not an integer or the body is not a JSON object.

### Accept a shared quote

 - [POST /quote:public/{id}/accept/{shareCode}](https://docs.transportial.com/apis/quote/accept:quote:public.md): Lets the recipient accept a quote through its public share link, without logging in. Sharing must be open on the quote and the share code must match.

Behaves like the authenticated accept: an open quote becomes accepted, the selected optional lines are accepted and the others declined (all stay pending without a selection), totals are recalculated, a linked transport order is accepted and an ACCEPTED activity is logged. A quote that is not open is returned unchanged. The body must carry consent: true and the observedContentHash from the quote, plus the name and e-mail address the platform requires (see the quote's acceptancePolicy). Every acceptance is recorded in the quote's agreement log (GET /quote/{id}/agreement): who accepted, from which IP address and browser, the consent statement, a hash of the exact content shown and, unless switched off, a sealed PDF of the quote as accepted.

Permission: Public, no authentication; access is granted by the share code.

Errors: 400 quote_acceptance_name_required, quote_acceptance_email_required, quote_acceptance_email_invalid, quote_acceptance_consent_required or quote_acceptance_expired (past the due date while the platform enforces it); 409 quote_changed when the quote changed since it was shown; 401 when sharing is closed, the share link has expired (validUntil) or the share code does not match; 404 when no quote with this id exists.

### Reject an extra charge of a shared quote

 - [POST /quote:public/{id}/charge/{lineId}/reject/{shareCode}](https://docs.transportial.com/apis/quote/reject:quote:charge:public.md): Extra charges are quote lines added after the price was agreed that need approval. Only approved charges count towards the totals, the expected revenue (ledger) and invoices. Lets the recipient reject a charge waiting for them (to_approve) through the quote's public share link, with a reason. Recorded in the agreement log; triggers afterRejectingCharge.

Permission: Public, no authentication; access is granted by the share code.

Errors: 400 charge_decision_not_allowed, charge_already_invoiced or charge_rejection_reason_required; 409 quote_changed; 401 when sharing is closed, the share link has expired or the share code does not match; 404 when the quote or charge does not exist.

### Send a quote

 - [POST /quote/{id}/send](https://docs.transportial.com/apis/quote/send:quote.md): Sends a quote to its recipient and marks it open, so it can be accepted or declined. An accepted or invoiced quote is only sent again; its status does not change.

When the administration has a quote-send message automation, it is executed with the quote PDF (in the recipient's preferred language) and the administration's standard quote documents as attachments. Integrations listening to sent quotes are triggered and a SENT activity is logged. The sending is also recorded in the quote's agreement log with a hash of the content and of the PDF that was sent. No request body.

Permission: send:quote

Errors: 404 when no quote with this id exists on your platform, or a customer-portal user may not see it (only quotes of their own businesses).

### Revise a sent quote

 - [POST /quote/{id}/revise](https://docs.transportial.com/apis/quote/revise:quote.md): Withdraws an open quote back to concept, so its lines and prices can be changed and the quote sent again. A linked transport order is not affected. The share code is renewed, so the link to the withdrawn version stops working; the revision is recorded in the quote's agreement log and a STATUS_UPDATED activity is logged. No request body.

Permission: update:quote

Errors: 400 when the quote is not open; 404 when no quote with this id exists on your platform, or a customer-portal user may not see it.

### Update a quote

 - [PUT /quote](https://docs.transportial.com/apis/quote/update:quote.md): Updates a quote. The body must contain the quote's id; the fields sent are merged into the stored quote and the rest is kept. When no quote with this id exists yet, it is created (upsert).

On save the totals and the quote's expected ledger entries are recalculated from the lines (entries of removed lines are deleted, and transport-order costs no longer absorbed by a line are unlocked); integrations listening to quote updates are triggered and an UPDATED activity is logged. Status changes are not validated here: use the send, accept and decline endpoints for their side effects.

Permission: update:quote

Errors: 400 when the body is not valid JSON or does not match the model; 404 when the id belongs to a quote of another platform, or to a quote a customer-portal user may not see; 409 when the quote was changed concurrently.

### Create a quote

 - [POST /quote](https://docs.transportial.com/apis/quote/create:quote.md): Creates an outgoing or incoming quote.

- The administration is taken from administration (an id, or an object with an id), else your active administration, else the platform's primary administration. On an outgoing quote subjectBusiness defaults to the administration's business, on an incoming one targetBusiness.
- An outgoing quote without name gets the next number of the administration's quote sequence; an incoming quote without name is named after the counterparty. The currency defaults to the platform's currency. Sub-totals, tax and totals are calculated from the lines, and every non-optional line is booked as an expected ledger entry (lines absorbed from a transport-order cost write their price back to that cost instead).
- You are recorded as creator, integrations listening to quote creation are triggered and a CREATED activity is logged.

Permission: create:quote

Errors: 400 when the body is not valid JSON or does not match the model; 404 when the request has no user session.

### Get a shared quote

 - [GET /quote:public/{id}/{shareCode}](https://docs.transportial.com/apis/quote/get:quote:public.md): Returns a quote through its public share link, for example to show it to a customer who is not logged in so they can accept or decline it. Sharing must be open on the quote and the share code must match.

Financial privacy: the link holder gets the customer view: no expectedCost, actualCost, pricingElements, capacityContract, ledgerEntries, lockedBudget, pricingModules, creditLimit or creditStatus anywhere in the response.

Permission: Public, no authentication; access is granted by the share code.

Errors: 401 when sharing is closed, the share link has expired (validUntil) or the share code does not match; 404 when no quote with this id exists.

### Get a quote

 - [GET /quote/{id}](https://docs.transportial.com/apis/quote/get:quote.md): Returns one quote of your platform with its lines, totals, parties, documents and external identifiers.

Portal users can only read their own quotes (customer accounts outgoing quotes addressed to them, supplier accounts the incoming quotes they sent); other quotes are reported as not found.

Permission: get:quote

Errors: 404 when no quote with this id exists on your platform, or a customer-portal user may not see it.

### Delete a quote

 - [DELETE /quote/{id}](https://docs.transportial.com/apis/quote/delete:quote.md): Soft-deletes a quote: it is marked as deleted and no longer listed or searchable. A DELETED activity is logged and integrations listening to quote updates are triggered.

Permission: delete:quote

Errors: 404 when no quote with this id exists on your platform, or a customer-portal user may not see it (only quotes of their own businesses).

### Reject an extra charge

 - [POST /quote/{id}/charge/{lineId}/reject](https://docs.transportial.com/apis/quote/reject:quote:charge.md): Extra charges are quote lines added after the price was agreed that need approval. Only approved charges count towards the totals, the expected revenue (ledger) and invoices. Rejects a charge waiting for the customer (to_approve); your own users may also reject one waiting for internal approval. Send the reason; it is required when the platform requires a rejection reason. A rejected charge never counts; your own users can offer it again with the submit endpoint. Recorded in the agreement log; triggers afterRejectingCharge.

Permission: decline:quote

Errors: 400 charge_decision_not_allowed, charge_already_invoiced, charge_rejection_reason_required; 409 quote_changed; 404 when the quote or charge does not exist or is not visible to you.

### Accept an optional line of a shared quote

 - [POST /quote:public/{id}/option/{lineId}/accept/{shareCode}](https://docs.transportial.com/apis/quote/accept:quote:option:public.md): Lets the recipient decide through the quote's public share link, without logging in. Accepts one optional line of a quote. An accepted option counts like a regular line: it is included in the totals, the ledger and the invoice. The totals and the expected ledger entries are recalculated, and an ACCEPTED activity is logged. Options can be decided while the quote is concept, open or accepted, also after the transport order was accepted. The decision is a new acceptance: the body must carry consent: true, the observedContentHash and the name and e-mail address the platform requires, and it is recorded in the quote's agreement log.

Permission: Public, no authentication; access is granted by the share code.

Errors: 400 when the quote is declined, cancelled or invoiced, or quote_acceptance_name_required, quote_acceptance_email_required, quote_acceptance_email_invalid, quote_acceptance_consent_required, quote_acceptance_expired; 409 quote_changed when the quote changed since it was shown; 401 when sharing is closed, the share link has expired or the share code does not match; 404 when the quote or the optional line does not exist.

### Accept an optional quote line

 - [POST /quote/{id}/option/{lineId}/accept](https://docs.transportial.com/apis/quote/accept:quote:option.md): Accepts one optional line of a quote. An accepted option counts like a regular line: it is included in the totals, the ledger and the invoice. The totals and the expected ledger entries are recalculated, and an ACCEPTED activity is logged. Options can be decided while the quote is concept, open or accepted, also after the transport order was accepted. Once the quote is accepted, a decision is a new acceptance: it is recorded in the quote's agreement log with a sealed PDF of the result, and it is refused when the platform requires customer acceptance. A customer-portal user must send consent: true and the observedContentHash.

Permission: accept:quote

Errors: 400 when the quote is declined, cancelled or invoiced, or quote_customer_acceptance_required when only the customer may decide on an accepted quote; 409 quote_changed when an observedContentHash is sent that no longer matches; 404 when the quote does not exist or is not visible to you, or has no optional line with this id.

### Accept a quote

 - [POST /quote/{id}/accept](https://docs.transportial.com/apis/quote/accept:quote.md): Accepts an open quote on behalf of the recipient. Internal users can also accept a concept quote that was never sent, e.g. when the price was agreed by phone.

The quote becomes accepted. When selectedOptionalLineIds is sent, the listed optional lines are accepted and all other optional lines declined; without it the optional lines stay pending, to be decided per line later. Totals are recalculated, a requested or concept transport order linked to the quote is accepted too, and an ACCEPTED activity is logged. A quote in any other status is returned unchanged. The body is optional. Every acceptance is recorded in the quote's agreement log (GET /quote/{id}/agreement): who accepted, from which IP address and browser, the consent statement, a hash of the exact content shown and, unless switched off, a sealed PDF of the quote as accepted. A customer-portal user must send consent: true and the observedContentHash; your own users record the acceptance on the customer's behalf without them, unless the platform requires customer acceptance (financialSettings.quoteAcceptanceSettings.requireCustomerAcceptance).

Permission: accept:quote

Errors: 400 quote_customer_acceptance_required when only the customer may accept; for portal users 400 quote_acceptance_name_required, quote_acceptance_email_required, quote_acceptance_email_invalid, quote_acceptance_consent_required or quote_acceptance_expired; 409 quote_changed when observedContentHash no longer matches the quote; 404 when no quote with this id exists on your platform, or a customer-portal user may not see it (only quotes of their own businesses).

### Approve an extra charge

 - [POST /quote/{id}/charge/{lineId}/approve](https://docs.transportial.com/apis/quote/approve:quote:charge.md): Extra charges are quote lines added after the price was agreed that need approval. Only approved charges count towards the totals, the expected revenue (ledger) and invoices. Approves a charge waiting for the customer (to_approve); your own users may also approve one waiting for internal approval. A customer-portal user must send consent: true and the observedContentHash, like an option decision; your own users record the approval on the customer's behalf unless the platform requires customer acceptance. The decision is recorded in the quote's agreement log and triggers afterApprovingCharge. When the quote was already invoiced, the charge goes on a new concept invoice (setting invoiceLateApprovals).

Permission: accept:quote

Errors: 400 charge_decision_not_allowed, charge_already_invoiced, quote_customer_acceptance_required or a missing consent, name or e-mail; 409 quote_changed; 404 when the quote or charge does not exist or is not visible to you.

### Get the agreement log of a quote

 - [GET /quote/{id}/agreement](https://docs.transportial.com/apis/quote/get:quote:agreement.md): Returns the evidence of what was agreed on a quote, oldest first: every send, revision, acceptance, decline and option decision, with who answered (typed name, e-mail, user), through which channel, from which IP address and browser, the consent statement, a SHA-256 hash of the exact content shown and a link to the stored PDF of the quote as agreed.

The entries form a hash chain: each entry's hash covers its content and the previous entry's hash, and the database refuses changes to stored entries. integrity reports the result of recomputing the chain; currentContentHash shows whether the quote changed after the last entry.

Permission: get:quote; not available to customer-portal users.

Errors: 404 when no quote with this id exists on your platform, or you are a customer-portal user.

### Decline a quote

 - [POST /quote/{id}/decline](https://docs.transportial.com/apis/quote/decline:quote.md): Declines an open quote on behalf of the recipient. Internal users can also decline a concept quote that was never sent.

The quote becomes declined with the given reason as declinedReason; a requested or concept transport order linked to the quote is declined too, and a DECLINED activity is logged. A quote in any other status is returned unchanged. The decline is recorded in the quote's agreement log.

Permission: decline:quote

Errors: 400 when the body is missing or not a JSON object (send {} to decline without a reason); 409 quote_changed when an observedContentHash is sent that no longer matches; 404 when no quote with this id exists on your platform, or a customer-portal user may not see it (only quotes of their own businesses).

### Create an incoming quote from documents

 - [PUT /quote/from-documents](https://docs.transportial.com/apis/quote/create:quote:fromdocuments.md): Reads uploaded files (for example a carrier's quote PDF) with the platform's AI model and turns them into an incoming (in) quote of the primary administration.

Lines are linked to trips, consignments or transport orders when their texts mention a recognisable reference. When an existing quote matches the result (by id, name, description, number or external identifier) it is updated with it; otherwise a new quote is saved with the files attached as documents. The usual save behaviour applies (totals, ledger entries, integrations, activity).

Permission: create:quote

Errors: 400 when the body is not valid JSON.

### Get the activity timeline of a quote

 - [GET /quote/{id}/activity](https://docs.transportial.com/apis/quote/get:quote:activity.md): Returns everything recorded for a quote, newest first: creation, updates, sending, acceptance, decline and deletion, with the user who performed each action, and line changes.

Each entry carries its field-level changes (old and new value per field), the part it happened on (subjectType, subjectLabel, e.g. a stop or a line) and its origin (source: user, API, integration or system). Paginated with the offset and amount query parameters; totalResults is the total number of entries.

Permission: get:quote

Errors: 404 when no quote with this id exists on your platform, or a customer-portal user may not see it (only quotes of their own businesses).

### Create an invoice from a quote

 - [POST /quote/{id}/create:invoice](https://docs.transportial.com/apis/quote/create:quote:invoice.md): Creates and saves an invoice from a quote and marks the quote invoiced.

The invoice copies the quote's administration, parties, team, currency, description (the quote number becomes the invoice reference) and its non-optional lines with their pricing categories; its direction follows the quote (an incoming quote gives an incoming invoice) and the price counts as agreed. When the quote belongs to a transport order (and is not a trip's spot quote), every line is attributed to that order and the invoice is linked to it. The invoice is then saved like a newly created invoice (numbered, totals and ledger entries calculated). The quote's status is not checked; calling this again on an invoiced quote returns its existing invoice. No request body.

Permission: send:quote

Errors: 404 when no quote with this id exists on your platform, or a customer-portal user may not see it (only quotes of their own businesses).

### Decline an optional line of a shared quote

 - [POST /quote:public/{id}/option/{lineId}/decline/{shareCode}](https://docs.transportial.com/apis/quote/decline:quote:option:public.md): Lets the recipient decide through the quote's public share link, without logging in. Declines one optional line of a quote. A declined option stays on the quote but is left out of the totals, the ledger and the invoice. The totals and the expected ledger entries are recalculated, and a DECLINED activity is logged. Options can be decided while the quote is concept, open or accepted, also after the transport order was accepted. The decision is a new acceptance: the body must carry consent: true, the observedContentHash and the name and e-mail address the platform requires, and it is recorded in the quote's agreement log.

Permission: Public, no authentication; access is granted by the share code.

Errors: 400 when the quote is declined, cancelled or invoiced, or quote_acceptance_name_required, quote_acceptance_email_required, quote_acceptance_email_invalid, quote_acceptance_consent_required, quote_acceptance_expired; 409 quote_changed when the quote changed since it was shown; 401 when sharing is closed, the share link has expired or the share code does not match; 404 when the quote or the optional line does not exist.

### Decline a shared quote

 - [POST /quote:public/{id}/decline/{shareCode}](https://docs.transportial.com/apis/quote/decline:quote:public.md): Lets the recipient decline a quote through its public share link, without logging in. Sharing must be open on the quote and the share code must match.

An open quote becomes declined with the given reason; a requested or concept transport order linked to it is declined too and a DECLINED activity is logged. A quote that is not open is returned unchanged. Send the observedContentHash from the quote; the decline is recorded in the quote's agreement log with the given name, IP address and browser.

Permission: Public, no authentication; access is granted by the share code.

Errors: 400 when the body is missing or not a JSON object, or quote_acceptance_expired when past the due date while the platform enforces it; 409 quote_changed when the quote changed since it was shown; 401 when sharing is closed, the share link has expired (validUntil) or the share code does not match; 404 when no quote with this id exists.

## Invoice

Sales (`out`) and purchase (`in`) invoices of an administration. Each line has a quantity, a unit price in major currency units and a tax rate; sub-total, tax, total and open amount are recalculated on every save, and every line is booked as a ledger entry linked to the trip, consignment or order it bills. An outgoing invoice is numbered from its administration's number format and moves from concept to open when sent. It is reminded up to three times through message automations and becomes paid when the open amount reaches zero; it can also be shared and paid online through Stripe, Mollie or Adyen. An incoming invoice can be created from documents or e-mail by AI, and goes through an approval flow with the managers of its team.

### Confirm the payment of a shared invoice

 - [POST /invoice:public/{id}/{shareCode}/confirm-payment](https://docs.transportial.com/apis/invoice/confirm:invoice:public:payment.md): Confirms a completed online payment through the public share link, after verifying it with the payment provider. A verified payment is booked on the invoice: the invoice becomes paid when nothing is left open, the administration's invoice-paid message automation runs when configured, and a PAYMENT_RECEIVED activity is logged. An invoice that is already paid is returned unchanged.

- Stripe: the PaymentIntent must carry this invoice's id and have status succeeded; the whole open amount is booked.
- Mollie: the payment is fetched from Mollie and must be paid and created for this invoice; its amount is booked once per Mollie payment.
- Adyen: the browser's resultCode cannot be verified, so nothing is booked here: an Authorised, Pending or Received result is acknowledged with the unchanged invoice, and the signed Adyen webhook books the payment.

Permission: Public, no authentication; access is granted by the share code.

Errors: 400 when the provider-specific id is missing, the payment has not succeeded, the payment belongs to another invoice, the Adyen resultCode is not a success code, or the provider is unknown; 401 when sharing is disabled, the share link has expired (validUntil) or the share code does not match; 404 when no invoice with this id exists.

### List invoices of an administration

 - [GET /invoices/{administration}/{offset}/{amount}](https://docs.transportial.com/apis/invoice/get:invoices.md): Returns the invoices (outgoing and incoming) of one administration, newest first, plus the total count. Deleted and archived invoices are excluded.

Customer accounts only get outgoing invoices addressed to their businesses, supplier accounts only the incoming invoices their businesses sent; users with team-restricted visibility only get invoices of their teams. An unknown administration id returns an empty list.

Permission: get:invoices

Errors: 400 when offset is not an integer.

### Search invoices

 - [POST /invoices/{administration}/{offset}/{amount}](https://docs.transportial.com/apis/invoice/search:invoices.md): Searches the invoices of your platform in one direction (outgoing by default) with free text, status, date, due-date, counterparty, overdue and team filters, sorted by relevance or by the requested fields. Deleted and archived invoices are excluded.

When the administration path segment is the id of one of your administrations only its invoices are searched; an unknown id searches all administrations. Portal users only see their own invoices, whatever direction they send: customer accounts our outgoing invoices addressed to their businesses, supplier (carrier) accounts the incoming invoices their businesses sent us; users with team-restricted visibility only see invoices of their teams. A failing search returns an empty page.

Permission: search:invoices

Errors: 400 when offset is not an integer or the body is not a JSON object.

### Start paying a shared invoice

 - [POST /invoice:public/{id}/{shareCode}/payment-intent](https://docs.transportial.com/apis/invoice/create:invoice:public:paymentintent.md): Starts an online payment of the invoice's open amount (totalOpen) with one of the platform's payment providers, through the public share link.

- Stripe: creates a PaymentIntent for the open amount (converted to cents) and returns its client secret and the publishable key for Stripe.js.
- Mollie: creates a payment and returns the hosted checkout URL; returnUrl is required.
- Adyen: creates a checkout session and returns the data for Adyen's web components.

Nothing is recorded on the invoice yet; call the confirm-payment endpoint after the payer completes the payment.

Permission: Public, no authentication; access is granted by the share code.

Errors: 400 when the invoice is not open, reminded or too_late, no payment provider is configured, the requested provider is not configured, or returnUrl is missing for Mollie; 401 when sharing is disabled, the share link has expired (validUntil) or the share code does not match; 404 when the invoice or its platform does not exist.

### List my pending invoice approvals

 - [GET /invoice/pending-approvals](https://docs.transportial.com/apis/invoice/get:invoice:pendingapprovals.md): Returns the pending invoice approval requests addressed to the current user on your platform, highest priority first and then oldest request first.

Permission: get:pending_approvals

### Request approval of an incoming invoice

 - [POST /invoice/{invoiceId}/request-approval](https://docs.transportial.com/apis/invoice/request:invoice:approval.md): Asks a user to approve an incoming (in) invoice. The invoice becomes pending_approval, the approver is notified by push and e-mail, and an APPROVAL_REQUESTED activity is logged. If the invoice already has a pending approval, that approval is returned and nothing changes.

Permission: request:invoice_approval

Errors: 400 when approverId is missing, the approver does not exist on your platform, or the invoice is not an incoming invoice; 404 when no invoice with this id exists on your platform.

### Update an invoice

 - [PUT /invoice](https://docs.transportial.com/apis/invoice/update:invoice.md): Updates an invoice. The body must contain the invoice's id; the fields sent are merged into the stored invoice and the rest is kept. When no invoice with this id exists yet, it is created (upsert).

On save the totals, open amount and ledger entries are recalculated from the lines and nextReminderDate is reset to dueAt; integrations listening to invoice updates are triggered and an UPDATED activity is logged. Users with team-restricted visibility cannot change the invoice's team: it is kept as stored.

Permission: update:invoice

An issued outgoing invoice (see issuedAt) keeps its content: only status progress (open, reminded, too_late, paid), cancelledReason, team, matchedQuote, documents, shareSettings, identifiers and line attribution (entityType/entityId, pricingCategory) can change. Correct anything else with POST /invoice/{id}/credit-and-reissue.

Errors: 400 when the body is not valid JSON or does not match the model, with invoice_issued_locked (params.fields lists the refused fields) or invoice_issued_status_not_allowed (back to concept, or cancelled) for an issued outgoing invoice, and with invoice_period_closed (params.closedUntil, params.side) when the change touches a booking in a closed period of the administration or the policy refuses a booking date in one; 404 when the id belongs to an invoice of another platform; 409 when the invoice was changed concurrently.

### Create an invoice

 - [POST /invoice](https://docs.transportial.com/apis/invoice/create:invoice.md): Creates an outgoing or incoming invoice.

- The administration is taken from administration (id), else your active administration, else the platform's primary administration. On an outgoing invoice subjectBusiness is set to the administration's business, on an incoming one targetBusiness.
- An outgoing invoice without name gets the next number of the administration (from its numberFormat). The currency defaults to the platform's currency. Line ids are generated by the server; sub-totals, tax, totals and the open amount are calculated from the lines, and every line is booked as a ledger entry.
- An incoming concept invoice whose team (given, or found through the lines' entity references) has managers becomes pending_approval: an approval request is created for every manager and they are notified by push and e-mail.
- Integrations listening to invoice creation are triggered and a CREATED activity is logged.
- When transportOrder (an id) is sent, the invoice is linked to that transport order on your platform and, if it has no team, takes the order's team.

Permission: create:invoice

Errors: 400 when the body is not valid JSON or does not match the model, or with invoice_period_closed when the booking date falls in a closed period of the administration whose closedPeriodPolicy is reject; 404 when the request has no user session.

### Reject an incoming invoice

 - [POST /invoice/{invoiceId}/reject](https://docs.transportial.com/apis/invoice/reject:invoice.md): Rejects the latest pending approval request of an invoice. Only the user the request is addressed to can reject it. The approval becomes rejected with the given reason, the invoice becomes rejected and a REJECTED activity is logged.

Permission: reject:invoice

Errors: 400 when rejectionReason is missing, the invoice has no pending approval or you are not its approver; 404 when no invoice with this id exists on your platform.

### Get an invoice

 - [GET /invoice/{id}](https://docs.transportial.com/apis/invoice/get:invoice.md): Returns one invoice of your platform by id, with its lines, totals, parties, documents and external identifiers.

Users that may only see their own teams' invoices (team-restricted visibility) and portal users only get their own invoices (customer accounts outgoing invoices addressed to them, supplier accounts the incoming invoices they sent, without the internal pendingApproverIds); other invoices are reported as not found.

Permission: get:invoice

Errors: 404 when no invoice with this id exists on your platform or you are not allowed to see it.

### Delete an invoice

 - [DELETE /invoice/{id}](https://docs.transportial.com/apis/invoice/delete:invoice.md): Soft-deletes an invoice: it is marked as deleted and no longer listed or reminded. A DELETED activity is logged and integrations listening to invoice updates are triggered.

Permission: delete:invoice

An issued outgoing invoice (see issuedAt) and an invoice booked in a closed period of its administration cannot be deleted.

Errors: 400 with invoice_issued_not_deletable for an issued outgoing invoice (correct it with POST /invoice/{id}/credit-and-reissue) or invoice_period_closed for an invoice booked in a closed period; 404 when no invoice with this id exists on your platform.

### Approve an incoming invoice

 - [POST /invoice/{invoiceId}/approve](https://docs.transportial.com/apis/invoice/approve:invoice.md): Approves the latest pending approval request of an invoice. Only the user the request is addressed to can approve it. The approval becomes approved, the invoice becomes approved (it can then be sent or paid) and an APPROVED activity is logged.

Permission: approve:invoice

Errors: 400 when the invoice has no pending approval or you are not its approver; 404 when no invoice with this id exists on your platform.

### Get the activity timeline of an invoice

 - [GET /invoice/{id}/activity](https://docs.transportial.com/apis/invoice/get:invoice:activity.md): Returns everything recorded for an invoice, newest first: creation, updates, sending, reminders, approval requests and decisions, payments and deletion, with the user who performed each action, and line changes.

Each entry carries its field-level changes (old and new value per field), the part it happened on (subjectType, subjectLabel, e.g. a stop or a line) and its origin (source: user, API, integration or system). Paginated with the offset and amount query parameters; totalResults is the total number of entries.

Permission: get:invoice

Errors: 404 when no invoice with this id exists on your platform.

### List payment providers of a shared invoice

 - [GET /invoice:public/{id}/{shareCode}/payment-providers](https://docs.transportial.com/apis/invoice/get:invoice:public:paymentproviders.md): Returns the online payment providers configured for the platform that issued the invoice (stripe, mollie, adyen), so the payment page can offer them. The invoice must have sharing enabled and the share code must match.

Permission: Public, no authentication; access is granted by the share code.

Errors: 401 when sharing is disabled, the share link has expired (validUntil) or the share code does not match; 404 when the invoice or its platform does not exist.

### Send an invoice

 - [POST /invoice/{id}/send](https://docs.transportial.com/apis/invoice/send:invoice.md): Sends an invoice to its recipient.

A concept or approved invoice becomes open; other statuses are kept. When the administration has an invoice-send message automation, it is executed with the invoice PDF and the administration's standard invoice documents as attachments. Integrations listening to sent invoices are triggered and a SENT activity is logged. No request body.

When the customer business has an externalInvoiceAddress, the invoice is delivered into the customer's incoming invoices on their Transportial platform instead of by e-mail or Peppol.

Permission: send:invoice

Errors: 400 with invoice_inbox_link_invalid when the customer's Transportial invoice address is closed, unknown, or on your own platform (nothing is sent), peppol_invoice_invalid when the e-invoice is not valid Peppol BIS; 404 when no invoice with this id exists on your platform.

### Get a shared invoice

 - [GET /invoice:public/{id}/{shareCode}](https://docs.transportial.com/apis/invoice/get:invoice:public.md): Returns an invoice through its public share link, for example to show it to a customer who is not logged in. The invoice must have sharing enabled and the share code must match.

Financial privacy: the link holder gets the customer view: no expectedCost, actualCost, pricingElements, capacityContract, ledgerEntries, lockedBudget, pricingModules, creditLimit or creditStatus anywhere in the response.

Permission: Public, no authentication; access is granted by the share code.

Errors: 401 when sharing is disabled, the share link has expired (validUntil) or the share code does not match; 404 when no invoice with this id exists.

### Receive an invoice by e-mail

 - [PUT /invoice/mail/receiver](https://docs.transportial.com/apis/invoice/create:invoice:frommail.md): Inbound e-mail webhook (SendGrid Inbound Parse format) that turns an e-mailed invoice into an incoming (in) invoice.

The platform is taken from the recipient address (@...transportial.com) and the sender's domain must be one of the platform's allowed e-mail domains. The attachments are stored as files and the request is acknowledged immediately; in the background the e-mail body and attachments are read with the platform's AI model and saved as an incoming invoice of the primary administration (an existing matching invoice is updated instead), so processing errors are not reported to the caller.

Permission: Public, no authentication.

Errors: 400 when the platform cannot be found, the sender address is missing or invalid, or the sender's domain is not allowed.

### Download an invoice as UBL

 - [GET /invoice/{id}/ubl](https://docs.transportial.com/apis/invoice/get:invoice-ubl.md): Returns an outgoing invoice as a Peppol BIS Billing 3.0 UBL document (EN 16931), with the invoice PDF embedded. A negative invoice is returned as a UBL CreditNote. The document is generated on request and not validated; invoices sent over Peppol are validated before they leave.

Permission: get:invoice

Errors: 400 with ubl_outgoing_only for an incoming invoice; 404 when the invoice does not exist on your platform or, for a customer or supplier account, is not addressed to or issued by one of its businesses.

### List all pending invoice approvals

 - [GET /invoice/all-pending-approvals](https://docs.transportial.com/apis/invoice/get:invoice:allpendingapprovals.md): Returns all pending invoice approval requests on your platform, regardless of approver, highest priority first and then oldest request first. Intended for finance administrators.

Permission: get:all_pending_approvals

### Create an incoming invoice from documents

 - [PUT /invoice/from-documents](https://docs.transportial.com/apis/invoice/create:invoice:fromdocuments.md): Reads uploaded invoice documents (PDFs or images) with the platform's AI model and turns them into an incoming (in) invoice of the primary administration. Available as POST and PUT.

References in the text (trip, consignment or transport order numbers) are linked to the lines, unit prices and tax rates are filled in, and the supplier is resolved on your platform. If the document matches an existing invoice (same id, number, reference, payment reference or external identifier, with the same direction and supplier) that invoice is updated; otherwise a new invoice is created with the files attached as documents. Saving runs the normal invoice logic, including the approval flow.

The response lists the single resulting invoice; totalResults is not filled in.

Permission: create:invoice

Errors: 400 when the body is not valid JSON.

### Credit an issued invoice and start its replacement

 - [POST /invoice/{id}/credit-and-reissue](https://docs.transportial.com/apis/invoice/creditandreissue:invoice.md): Corrects an issued outgoing invoice, which can no longer be edited: creates a full credit note for it (every line with its quantity negated, creditedInvoiceId pointing at the original) and a new concept invoice with the original's lines, parties and payment term, dated today. Both are numbered and saved as concepts; the credit note settles the original's open amount. Edit the concept, then send both to the customer. Both are listed under the transport orders the original belongs to. No request body.

Permission: create:invoice

Errors: 400 with invoice_not_creditable when the invoice is incoming, not issued yet (edit the concept instead), cancelled or itself a credit note; invoice_period_closed when today falls in the administration's closed revenue period and its policy refuses; 404 when no invoice with this id exists on your platform.

### Send a payment reminder

 - [POST /invoice/{id}/send:reminder](https://docs.transportial.com/apis/invoice/send:invoice:reminder.md): Sends the next payment reminder for an outgoing invoice.

The first, second and third reminder use the administration's corresponding reminder message automation (with the invoice PDF and standard documents attached, when configured); the invoice becomes reminded, reminderCount is increased and nextReminderDate is moved forward. After three reminders no further reminder is sent. A REMINDER_SENT activity is logged. The same reminders are also sent automatically by a daily job once nextReminderDate is reached. No request body.

Permission: send:invoice

Errors: 400 when the invoice is not an outgoing invoice; 404 when no invoice with this id exists on your platform.

## Pricing

Pricings are a customer's rate agreements: a set of pricing modules (rate rules such as fixed, per km/unit, time, rate table, percentage, formula, Excel, variable-index adjustment and subscription) linked to businesses, with a currency and tax rate rules. Each module produces lines of quantity x unit price for the entity being priced (trip, transport order, vehicle, fleet vehicle, chauffeur, ...). Enabled pricings are invoiced automatically, periodically by the scheduler (triggerType endOfWeek, endOfMonth, ...) or when a trip or transport order finishes, and non-direct pricings drive the expected revenue of trips and orders. Pricing variable indexes (for example a diesel index) are named values that formula and variable-adjustment modules reference, and AI can propose modules from rate-card documents.

### Update a pricing

 - [PUT /pricing](https://docs.transportial.com/apis/pricing/update:pricing.md): Updates the pricing identified by the id in the body. Fields in the body overwrite the stored values;
fields left out keep their value. Sending modules or taxRates replaces those lists. When no pricing with
that id exists on your platform, a new pricing is created with it.

Permission: update:pricing

Errors: 400 when the body is not valid JSON or a module has a missing or unknown type; 404 when the
id belongs to a pricing of another platform.

### Create a pricing

 - [POST /pricing](https://docs.transportial.com/apis/pricing/create:pricing.md): Creates a pricing: a set of pricing modules (rate rules) that the platform uses to price and invoice
work for the businesses it is linked to. The modules, tax rates and inline documents in the body are saved
together with the pricing.

An enabled pricing is picked up automatically: time-based trigger types (endOfWeek, endOfMonth, ...) are
invoiced by the scheduler once their period has closed, endOfTrip / endOfTransportOrder pricings when a
trip or transport order of one of its businesses finishes. Non-direct pricings are also used to calculate the
expected revenue of trips and transport orders of their businesses.

Permission: create:pricing

Errors: 400 when the body is not valid JSON or a module has a missing or unknown type.

### Get a pricing variable index

 - [GET /pricingVariableIndex/{id}](https://docs.transportial.com/apis/pricing/get:pricingvariableindex.md): Returns one pricing variable index of your platform.

Permission: get:pricingVariableIndex

Errors: 404 when no pricing variable index with this id exists on your platform.

### Delete a pricing variable index

 - [DELETE /pricingVariableIndex/{id}](https://docs.transportial.com/apis/pricing/delete:pricingvariableindex.md): Permanently deletes a pricing variable index of your platform and returns it as it was before deletion.

Permission: delete:pricingVariableIndex

Errors: 404 when no pricing variable index with this id exists on your platform.

### List pricing variable indexes

 - [GET /pricingVariableIndexes/{offset}/{amount}](https://docs.transportial.com/apis/pricing/get:pricingvariableindexes.md): Returns one page of the pricing variable indexes of your platform, most recently updated first.
Archived and deleted indexes are left out. totalResults is the number of indexes across all pages.

Permission: get:pricingVariableIndexes

Errors: 400 when offset is not a number.

### Search pricing variable indexes

 - [POST /pricingVariableIndexes/{offset}/{amount}](https://docs.transportial.com/apis/pricing/search:pricingvariableindexes.md): Returns one page of the pricing variable indexes of your platform, most recently updated first.

The request body is currently not evaluated: this endpoint returns the same page as
GET /pricingVariableIndexes/{offset}/{amount}. totalResults is the number of indexes across all pages.

Permission: get:pricingVariableIndexes

Errors: 400 when offset is not a number.

### Preview generated VAT rules

 - [POST /pricing/taxRates:generate](https://docs.transportial.com/apis/pricing/update:pricing:taxrates:generate.md): Generates VAT rules for goods transport as pricing tax rate rules and returns them without saving anything.

The rules follow the place-of-supply rules of the EU VAT Directive (2006/112/EC) for the home country of the
chosen scheme: EU business customers with a VAT number get reverse charge (art. 44 and 196), business customers
outside the EU are out of scope (art. 44), transport to or from outside the EU is zero rated / exempt
(art. 146(1)(e) and 144), private customers pay the VAT of the departure country (art. 49 and 50) and everything
else gets the home rate. Each rule filters on the derived vatContext of the billed transport order or trip and
applies the tax rate mapped to its scenario; scenarios left out of mapping get a proposed tax rate (matched by
VAT category and percentage). With pricingIds or allPricings the response also lists the target pricings and
warnings about them.

Permission: update:pricing

Errors: 400 when there is no scheme for the country (vat_rules_preset_not_found); 404 when the administration does not exist on your platform.

### Get a pricing

 - [GET /pricing/{id}](https://docs.transportial.com/apis/pricing/get:pricing.md): Returns one pricing of your platform with its modules (ordered by sequenceNr), tax rates, businesses
and documents.

Permission: get:pricing

Errors: 404 when no pricing with this id exists on your platform.

### Delete a pricing

 - [DELETE /pricing/{id}](https://docs.transportial.com/apis/pricing/delete:pricing.md): Permanently deletes a pricing of your platform together with its modules and returns the pricing as it
was before deletion.

Permission: delete:pricing

Errors: 404 when no pricing with this id exists on your platform.

### List the VAT rule generator schemes

 - [GET /pricing/taxRates:presets](https://docs.transportial.com/apis/pricing/get:pricing:taxrates:presets.md): Returns the country schemes (presets) and the scenarios of the VAT rule generator, and the EU member states the
generated rules are evaluated with (platform pricing settings, vatRuleSettings.euMemberStates).

Permission: get:pricing

### Generate pricing modules from documents

 - [POST /pricing/modules/from-documents](https://docs.transportial.com/apis/pricing/create:pricing:modules:fromdocuments.md): Uses the platform's AI model to read rate cards, contracts or price lists (uploaded files) and/or a text
description and proposes pricing modules for them. Nothing is saved: the response contains module objects in
the same JSON shape as PricingModel.modules, ready to be reviewed and sent with a create or update pricing
request. Categories mentioned by the AI are matched to the pricing categories of your platform where possible.

Pass the targetEntityType and currency of the pricing being edited so the modules bill at that level and in
that currency. The modules target the current pricing engine (engineVersion 2). They are validated (enum values,
selectors against the selector catalogue of GET /pricing/selectors/{entityType}, filter chains, tables, formulas);
problems are sent back to the AI once, and what remains is returned in warnings.

File ids in documents that are unknown, deleted or owned by another platform are skipped.

Permission: create:pricing

Errors: 400 when the body is not valid JSON.

### Update a pricing variable index

 - [PUT /pricingVariableIndex](https://docs.transportial.com/apis/pricing/update:pricingvariableindex.md): Updates the pricing variable index identified by the id in the body; fields left out keep their value.
A new value is used by every pricing module that references the index from the next calculation on. When no
index with that id exists on your platform, a new one is created with it.

Permission: update:pricingVariableIndex

Errors: 400 when the body is not valid JSON; 404 when the id belongs to an index of another platform.

### Create a pricing variable index

 - [POST /pricingVariableIndex](https://docs.transportial.com/apis/pricing/create:pricingvariableindex.md): Creates a pricing variable index: a named numeric value (for example a diesel price index) that pricing
modules reference. Formula modules substitute it by its variableName; variable adjustment modules adjust
their price by how far the index is from a base level.

Permission: create:pricingVariableIndex or update:pricingVariableIndex

Errors: 400 when the body is not valid JSON.

### Apply generated VAT rules to pricings

 - [POST /pricing/taxRates:apply](https://docs.transportial.com/apis/pricing/update:pricing:taxrates:apply.md): Generates the VAT rules exactly like the preview and stores them on the target pricings (pricingIds, or every
pricing of the platform with allPricings).

On each pricing the previously generated rules (generated true) are replaced and hand-written rules are kept;
placement puts the generated rules before (first, default) or after (last) them. A regenerated rule keeps
the id of the rule it replaces, so applying the same input again changes nothing.

Permission: update:pricing

Errors: 400 when there is no scheme for the country (vat_rules_preset_not_found), no pricing is selected (vat_rules_no_target_pricings) or a required scenario has no tax rate (vat_rules_scenario_unmapped); 404 when the administration does not exist on your platform.

### List pricings

 - [GET /pricings/{offset}/{amount}](https://docs.transportial.com/apis/pricing/get:pricings.md): Returns one page of the pricings of your platform, most recently updated first. Archived and deleted
pricings are left out. totalResults is the number of pricings across all pages.

Permission: get:pricings

Errors: 400 when offset is not a number.

### Search pricings

 - [POST /pricings/{offset}/{amount}](https://docs.transportial.com/apis/pricing/search:pricings.md): Search the pricings of your platform. query is matched against the name, description
and business names of each pricing; the other body fields filter on target entity type, trigger type, flags,
businesses and administration. Results are sorted by sortBy, newest first by default. Archived and deleted
pricings are left out. totalResults is the number of matches across all pages.

Permission: get:pricings

Errors: 400 when the body is not valid JSON or offset/amount is not a number.

### List pricing selectors of an entity type

 - [GET /pricing/selectors/{entityType}](https://docs.transportial.com/apis/pricing/get:pricing:selectors.md): Returns the curated selectors (GSJson paths) that pricing module filters, field modules, formulas and
percentage start values can use for one priced entity type, with their value type, unit and possible values. Paths are
evaluated on the JSON of the priced entity, so associations carry their .entity. wrapper. Count-based unit modules
filter per element: use the consignment, goods or action selectors for them.

Permission: get:pricing

Errors: 404 when the entity type is unknown.

## Transaction

A money movement recorded by the server: invoice payments synced from accounting integrations or recorded manually, credit charges and payouts. Transactions are created only by those flows and are read-only through the API. They are listed per receiving user, can be read individually by the users involved (or role level 100 and up), and can be summed per day.

### List transactions (business filter not applied)

 - [GET /transactions/business/{business}/{offset}/{amount}](https://docs.transportial.com/apis/transaction/get:transactions:bybusiness.md): Intended to list the transactions of a business, but the business path segment is currently ignored: it returns the transactions of your platform received by you (where you are toUser), newest first, plus the total count, exactly like GET /transactions/{offset}/{amount}.

Permission: get:transactions

Errors: 400 when offset is not an integer; 404 when the request has no user session.

### List your received transactions

 - [GET /transactions/{offset}/{amount}](https://docs.transportial.com/apis/transaction/get:transactions.md): Returns the transactions of your platform received by you (where you are toUser), newest first, plus the total count.

Permission: get:transactions

Errors: 400 when offset is not an integer; 404 when the request has no user session.

### List transactions received by a user

 - [GET /transactions/user/{user}/{offset}/{amount}](https://docs.transportial.com/apis/transaction/get:transactions:byuser.md): Returns the transactions of your platform received by the given user (where that user is toUser), newest first, plus the total count.

Permission: get:transactions

Errors: 400 when offset is not an integer; 404 when no user with this id exists on your platform.

### Get daily totals of your received transactions

 - [GET /transactions/date/{startDate}/{endDate}](https://docs.transportial.com/apis/transaction/get:transactions:dailytotals.md): Returns, per UTC calendar day, the sum and the number of the transactions of your platform received by you (where you are toUser) created from the start of startDate up to the start of endDate (so transactions on endDate itself are not counted). Days are returned newest first; days without transactions are left out. Amounts are summed as they are, without currency conversion. Despite the name, no averages are calculated.

Permission: get:transactions

Errors: 404 when the request has no user session; a date that cannot be parsed as yyyy-MM-dd fails the request.

### Get a transaction

 - [GET /transaction/{id}](https://docs.transportial.com/apis/transaction/get:transaction.md): Returns one transaction of your platform, with its external identifiers.

Only the user who recorded it, the user who sent or received it, and users with role level 100 or higher may read it.

Permission: get:transaction

Errors: 401 when you are not involved in the transaction and your role level is below 100; 404 when no transaction with this id exists on your platform or the request has no user session.

## LedgerEntry

A booking in the platform's financial ledger: an amount excluding tax on the debit (cost) or credit (revenue) side, either expected (from quotes, transport orders, trips and their pricing) or actual (from invoices). Entries are written by the server whenever those documents are saved, never through the API. Each entry lists the entities its amount is attributed to (with split factors and an owning source), which drives the revenue and cost reporting per trip, order, consignment, vehicle or business. These endpoints read entries by entity, by creation period, or by pricing category and period.

### List ledger entries created in a period

 - [POST /ledgerEntriesByDateRange](https://docs.transportial.com/apis/ledgerentry/get:ledgerentries:bydaterange.md): Returns all ledger entries of your platform created between from and to (inclusive), for example to report revenue and cost over a month. Note that the range is matched against the time the entry was created, not its business date.

type, state, administrationId and businessId narrow the result further. Deleted and archived entries are left out. Entries come newest first; set amount (with offset) to page through them, totalResults is then the number of all matching entries. Without amount every matching entry is returned, which is what totals must be computed from.

Permission: get:ledgerEntriesByDateRange

Errors: 400 when the body is not valid JSON, from or to is missing or not an ISO 8601 date-time, or an enum value is unknown.

### List ledger entries of a category in a period

 - [POST /ledgerEntriesByCategoryAndDateRange](https://docs.transportial.com/apis/ledgerentry/get:ledgerentries:bycategoryanddaterange.md): Returns all ledger entries of your platform in one pricing (revenue/cost) category that were created between from and to (inclusive), for example to report fuel surcharges over a month. The range is matched against the time the entry was created, not its business date.

type, state, administrationId and businessId narrow the result further. The result is not paginated: offset and amount in the body are ignored.

Permission: get:ledgerEntriesByCategoryAndDateRange

Errors: 400 when the body is not valid JSON, pricingCategoryId, from or to is missing or invalid, or an enum value is unknown.

### List ledger entries of an entity

 - [POST /ledgerEntriesByEntity](https://docs.transportial.com/apis/ledgerentry/get:ledgerentries:byentity.md): Returns all ledger entries of your platform that are attributed to one entity, for example everything booked on a trip, transport order, consignment, quote or business (expected and actual, revenue and cost). Use it to show the financial breakdown of that entity.

An entry matches when its relatedEntities contain the given entityId and entityType; type, state, administrationId and businessId narrow the result further. Deleted and archived entries are excluded. The result is not paginated: offset and amount in the body are ignored.

Permission: get:ledgerEntriesByEntity

Errors: 400 when the body is not valid JSON or contains an unknown enum value.

## ExchangeRate

Exchange rates of your platform for multi-currency administrations (feature `multiCurrency`). Each administration reports in a base currency (its own currency, else the platform currency); invoices and ledger entries in other currencies are converted to it. The rate for a date is the most recent one on or before that date within the platform's tolerance (`financialSettings.exchangeRateSettings`): manual rates first, then ECB euro reference rates. A missing ECB rate is fetched on demand and stored; when the ECB cannot be reached the document is saved unconverted and converted later. An invoice's rate is recalculated while it is a concept and frozen once it is issued.

### Get an exchange rate

 - [GET /exchange-rate/{id}](https://docs.transportial.com/apis/exchangerate/get:exchangerate.md): Returns one manual or ECB exchange rate of your platform.

Requires the multiCurrency feature on your platform; without it every exchange rate endpoint answers 400 with multi_currency_disabled.

Permission: get:exchangeRate

Errors: 400 with multi_currency_disabled; 404 when no non-deleted rate with this id exists on your platform.

### Update a manual exchange rate

 - [PUT /exchange-rate/{id}](https://docs.transportial.com/apis/exchangerate/update:exchangerate.md): Changes the currencies, rate or date of a manual rate; keys left out keep their value. Invoices that were already issued keep the rate they were issued with; concept invoices and ledger entries pick the change up on their next save.

Requires the multiCurrency feature on your platform; without it every exchange rate endpoint answers 400 with multi_currency_disabled.

Permission: update:exchangeRate

Errors: 400 with multi_currency_disabled, with exchange_rate_ecb_read_only for an ECB rate, or for the same validation errors as create; 404 when no non-deleted rate with this id exists on your platform.

### Delete an exchange rate

 - [DELETE /exchange-rate/{id}](https://docs.transportial.com/apis/exchangerate/delete:exchangerate.md): Deletes a manual or ECB rate. A deleted ECB rate is fetched again the next time a conversion needs it. Issued invoices keep the rate they were issued with.

Requires the multiCurrency feature on your platform; without it every exchange rate endpoint answers 400 with multi_currency_disabled.

Permission: delete:exchangeRate

Errors: 400 with multi_currency_disabled; 404 when no non-deleted rate with this id exists on your platform.

### Search exchange rates

 - [POST /exchange-rates/{offset}/{amount}](https://docs.transportial.com/apis/exchangerate/search:exchangerates.md): Lists the manual and ECB exchange rates of your platform, newest date first, optionally filtered on currency, source and a date range; total counts the matches across all pages.

Requires the multiCurrency feature on your platform; without it every exchange rate endpoint answers 400 with multi_currency_disabled.

Permission: get:exchangeRates

Errors: 400 with multi_currency_disabled, or when a filter value cannot be parsed.

### Create a manual exchange rate

 - [POST /exchange-rates](https://docs.transportial.com/apis/exchangerate/create:exchangerate.md): Adds a manual rate: one unit of fromCurrency is worth rate units of toCurrency on date (default today). Manual rates win over ECB rates when the server converts invoices and ledger entries to an administration's base currency, also when used inverted. source is always manual.

Requires the multiCurrency feature on your platform; without it every exchange rate endpoint answers 400 with multi_currency_disabled.

Permission: create:exchangeRate

Errors: 400 with multi_currency_disabled, or when a currency is not a 3-letter ISO 4217 code, both currencies are the same, rate is not greater than zero or date is not an ISO date.

## AccountingMappingRule

Rules deciding the ledger account, VAT code and dimensions (cost centre, cost unit, project, department, class) accounting integrations book invoice lines on. Every accounting connector (Exact Online, Twinfield, AFAS, Moneybird, SnelStart, e-Boekhouden, QuickBooks Online) resolves each line through the same rules: in `sequenceNr` order, the first active rule whose conditions all hold wins. Conditions: integration, administration, direction, pricing category, team, customer or supplier, its country and EU membership, transport mode of the billed trips and the VAT category of the line. Lines without a matching rule keep the codes of their pricing category and tax rate, so nothing changes until rules are added. The evaluate endpoint shows the outcome for an existing invoice without sending anything.

### Create an accounting mapping rule

 - [POST /accounting-mapping-rules](https://docs.transportial.com/apis/accountingmappingrule/create:accountingmappingrule.md): Adds a rule deciding the ledger account, VAT code and dimensions accounting integrations book matching invoice lines on. Without sequenceNr the rule is evaluated after the existing ones. References (administration, integration, pricingCategories, teams, businesses) are given by id and must belong to your platform.

Permission: create:accountingMappingRule

Errors: 400 when a reference, country, transport mode, VAT category or direction is unknown, when the rule sets no ledger account code, VAT code or dimension, or when dimensions are incomplete, duplicated or more than 10.

### Get an accounting mapping rule

 - [GET /accounting-mapping-rule/{id}](https://docs.transportial.com/apis/accountingmappingrule/get:accountingmappingrule.md): Returns one accounting mapping rule of your platform with the names of the entities it references.

Permission: get:accountingMappingRule

Errors: 404 when no non-deleted rule with this id exists on your platform.

### Update an accounting mapping rule

 - [PUT /accounting-mapping-rule/{id}](https://docs.transportial.com/apis/accountingmappingrule/update:accountingmappingrule.md): Changes a rule; keys left out keep their value, a list sent replaces the stored list. Invoices that were already pushed to an accounting package are not re-booked.

Permission: update:accountingMappingRule

Errors: 400 for the same validation errors as create; 404 when no non-deleted rule with this id exists on your platform.

### Delete an accounting mapping rule

 - [DELETE /accounting-mapping-rule/{id}](https://docs.transportial.com/apis/accountingmappingrule/delete:accountingmappingrule.md): Deletes a rule; lines it matched fall through to the next rule or to the pricing category codes on their next push.

Permission: delete:accountingMappingRule

Errors: 404 when no non-deleted rule with this id exists on your platform.

### Search accounting mapping rules

 - [POST /accounting-mapping-rules/{offset}/{amount}](https://docs.transportial.com/apis/accountingmappingrule/search:accountingmappingrules.md): Lists the accounting mapping rules of your platform in evaluation order, optionally filtered; total counts the matches across all pages.

Permission: get:accountingMappingRules

### Test accounting mapping rules on an invoice

 - [POST /accounting-mapping-rules/evaluate](https://docs.transportial.com/apis/accountingmappingrule/get:accountingmappingevaluation.md): Answers which ledger account, VAT code and dimensions each line of an existing invoice would be booked on, and by which rule. Nothing is saved or sent. Optionally tests an unsaved version of a rule (rule): it takes the place of the saved rule with the same id, and each line reports whether it matches and which conditions do not hold.

Permission: get:accountingMappingRules

Errors: 400 when integrationId is not an integration of your platform, or for the validation errors of create on rule; 404 when the invoice does not exist on your platform.

### Reorder accounting mapping rules

 - [PUT /accounting-mapping-rules/order](https://docs.transportial.com/apis/accountingmappingrule/update:accountingmappingruleorder.md): Sets the evaluation order: the listed rules first in the given order, rules left out after them in their current order. sequenceNr is renumbered from 0. Returns every rule in the new order.

Permission: update:accountingMappingRule

## Administration

An administration is a financial entity (legal company) of the platform, tied to one business. It owns the numbering of invoices and quotes, the payment term, the message automations used to send invoices, reminders and quotes, the localized terms and conditions, and a balance that charges increase and payouts decrease. Every invoice and quote belongs to one administration. Exactly one administration per platform is primary and is used whenever none is given; deleting the primary one promotes the oldest remaining one.

### List Transportial invoice addresses

 - [GET /administration/{id}/invoice-inbox-links](https://docs.transportial.com/apis/administration/get:administration-invoice-inbox-links.md): Returns the open Transportial invoice addresses of the administration, newest first. A supplier on another Transportial platform stores an address's url as externalInvoiceAddress on your business; its invoices then arrive as incoming invoices of this administration.

Permission: get:administration

### Create a Transportial invoice address

 - [POST /administration/{id}/invoice-inbox-links](https://docs.transportial.com/apis/administration/create:administration-invoice-inbox-link.md): Creates an invoice address for the administration and returns its url. Share it with a supplier that works on another Transportial platform: once they store it as externalInvoiceAddress on your business, every invoice they send you is delivered straight into your incoming invoices (supplier, lines, VAT and references included) and runs through your approval flow. Anyone holding the URL can deliver invoices, so share it only with that supplier; close it with DELETE /invoice-inbox-links/{id}.

Permission: update:administration

Errors: 400 when validUntil is not an ISO 8601 instant; 404 when the administration does not exist on your platform.

### Delete an administration

 - [DELETE /administration/{id}](https://docs.transportial.com/apis/administration/delete:administration.md): Soft-deletes an administration: it is marked as deleted and no longer listed. Its invoices and quotes are kept.

When the deleted administration was primary, the oldest remaining administration of the platform becomes primary.

Permission: delete:administration

Errors: 404 when no administration with this id exists on your platform.

### Get an administration

 - [GET /administration](https://docs.transportial.com/apis/administration/get:administration.md): Returns one administration of your platform by id, including its message automations and identifiers (bank accounts are not included).

Note: this route is registered without an {id} path segment while the handler reads the id path parameter, so calls currently fail until the path is corrected.

Permission: get:administration

Errors: 404 when no administration with this id exists on your platform.

### Update an administration

 - [PUT /administration](https://docs.transportial.com/apis/administration/update:administration.md): Updates an administration. The body must contain the administration's id; the fields sent are merged into the stored administration and the rest is kept. When no administration with this id exists yet, it is created (upsert).

Marking an administration as primary removes the flag from all other administrations of the platform; un-marking the only primary administration has no effect (a platform always keeps one primary).

Permission: update:administration

Errors: 400 when the body is not valid JSON or does not match the model; 409 when the administration was changed concurrently; 404 when the id belongs to an administration of another platform.

### Create an administration

 - [POST /administration](https://docs.transportial.com/apis/administration/create:administration.md): Creates an administration (legal entity) on your platform.

The referenced business is resolved on your platform. If no other administration of the platform is primary, the new one becomes primary; if primary is true, the primary flag is removed from all other administrations.

Permission: create:administration

### List administrations

 - [GET /administrations/{offset}/{amount}](https://docs.transportial.com/apis/administration/get:administrations.md): Returns a page of the administrations of your platform (deleted and archived administrations excluded), plus the total number of administrations.

Message automations and identifiers are included; bank accounts are not.

Permission: get:administrations

Errors: 400 when offset is not an integer.

### Search administrations

 - [POST /administrations/{offset}/{amount}](https://docs.transportial.com/apis/administration/search:administrations.md): Searches the administrations of your platform with free text, a creation-date range and a business filter, sorted as requested. Deleted and archived administrations are excluded.

Results are ordered by relevance when query is given and by creation date (newest first) otherwise, unless sortBy is set. A failing search returns an empty page.

Permission: search:administrations

Errors: 400 when offset or amount is not an integer or the body is not a JSON object.

### Close a Transportial invoice address

 - [DELETE /invoice-inbox-links/{id}](https://docs.transportial.com/apis/administration/delete:invoice-inbox-link.md): Closes the invoice address: suppliers that stored it can no longer deliver through it and their sends are refused with invoice_inbox_link_invalid. Invoices already received stay.

Permission: update:administration

### Get the Peppol registration of an administration

 - [GET /administration/{id}/peppol](https://docs.transportial.com/apis/administration/get:administration-peppol.md): Returns whether the administration is registered as a company at the platform's Recommand Peppol access point, and whether its identity check is complete. Only a verified company receives invoices over Peppol.

Permission: get:administration

Errors: 400 with peppol_integration_missing without an enabled Recommand integration; 404 when the administration does not exist on your platform.

### Register an administration on Peppol

 - [POST /administration/{id}/peppol/register](https://docs.transportial.com/apis/administration/register:administration-peppol.md): Registers the administration's business as a company at the platform's Recommand Peppol access point (once) and returns a verificationUrl. An authorised representative of the company completes the identity check at that link; only then is the company published on Peppol. Calling it again for a registered administration returns a fresh link. No request body.

Permission: update:administration

Errors: 400 with peppol_integration_missing without an enabled Recommand integration, peppol_company_incomplete when the business lacks a name, an operational base address or a CoC/VAT number, peppol_registration_failed when Recommand refuses the company; 404 when the administration does not exist on your platform.

### Request a new Peppol identity-check link

 - [POST /administration/{id}/peppol/verify](https://docs.transportial.com/apis/administration/verify:administration-peppol.md): Returns a fresh verificationUrl for an administration that is registered but not yet verified at the Recommand Peppol access point. No request body.

Permission: update:administration

Errors: 400 with peppol_integration_missing without an enabled Recommand integration, peppol_not_registered when the administration was never registered, peppol_registration_failed when Recommand refuses; 404 when the administration does not exist on your platform.

## PalletBalance

Pallet-exchange (EPAL) administration as a ledger of signed pallet movements per counterparty and pallet type: positive quantities mean the counterparty owes you pallets, negative ones mean you owe them. Movements are recorded manually or synced automatically from realized take/return actions, which replace their own synced movements each time they are synced. The balance summary sums all movements per counterparty and pallet type.

### Get pallet balances

 - [POST /pallet-balances/summary](https://docs.transportial.com/apis/palletbalance/get:palletbalances:summary.md): Returns the net pallet balance per counterparty and pallet type: the sum of the signed quantities of all movements (manual and synced from take/return actions). Positive balances mean the counterparty owes you pallets. Optionally limited to one counterparty. The body is optional.

Permission: get:pallet-movement

Errors: 400 when a non-empty body is not valid JSON.

### List pallet movements

 - [POST /pallet-movements/{offset}/{amount}](https://docs.transportial.com/apis/palletbalance/search:palletmovements.md): Returns one page of the pallet movements of your platform, newest date first, optionally only those with one counterparty. The body is optional. amount is used as given (not clamped); invalid paging returns an empty page.

Permission: get:pallet-movement

Errors: 400 when a non-empty body is not valid JSON, or offset or amount is not an integer.

### Record a pallet movement

 - [POST /pallet-movements](https://docs.transportial.com/apis/palletbalance/create:palletmovement.md): Records a manual pallet-exchange movement with a counterparty. The fields are stored as sent; the counterparty id is not checked against existing entities. The movement immediately counts towards the balance summary.

Permission: create:pallet-movement

Errors: 400 when the body is not valid JSON or does not match the schema.

### Get a pallet movement

 - [GET /pallet-movement/{id}](https://docs.transportial.com/apis/palletbalance/get:palletmovement.md): Returns one pallet movement of your platform.

Permission: get:pallet-movement

Errors: 404 when no pallet movement with this id exists on your platform.

### Delete a pallet movement

 - [DELETE /pallet-movement/{id}](https://docs.transportial.com/apis/palletbalance/delete:palletmovement.md): Permanently deletes the pallet movement, so it no longer counts towards the balance. Returns the movement as it was before deletion. Movements synced from a take/return action are recreated the next time that action is synced.

Permission: delete:pallet-movement

Errors: 404 when no pallet movement with this id exists on your platform.

## Payment

Public webhooks that payment providers (Adyen, Mollie, Stripe) call, one URL per platform, to report payment status changes asynchronously. Each request is checked first: Adyen by HMAC, Stripe by its signature header, Mollie by fetching the payment back from Mollie's API. A confirmed payment marks the referenced invoice as paid, and this is idempotent; Stripe setup confirmations also open the billing payment gate. Event types that are not handled are acknowledged so the provider does not retry them.

### Receive a Stripe webhook event

 - [POST /payment/webhook/stripe/{platformId}](https://docs.transportial.com/apis/payment/handle:payment:webhook:stripe.md): Endpoint for Stripe webhook events that confirm invoice payments and payment-method setups.

Payload: a Stripe Event object { "id": "evt_...", "type": "...", "data": { "object": { ... } } }, read as raw JSON (independent of the Stripe API version).

Verification: the Stripe-Signature header must be a valid Stripe signature of the raw body, with a timestamp at most 300 seconds old, checked against the webhookEndpointSecret of the platform's Stripe integration, or the server-wide STRIPE_WEBHOOK_SECRET for platforms billed through the directly configured Stripe account.

Effect:
- payment_intent.succeeded: pays the invoice named in the PaymentIntent's metadata.invoice_id, only when it belongs to platformId. amount_received (minor units) is applied to the invoice's open amount, capped at that open amount; an invoice settled in full becomes PAID, its administration's invoice-paid message automation is sent and a payment activity is logged.
- setup_intent.succeeded: marks the payment method of the platform billing whose intentId equals the SetupIntent id as confirmed, which opens the payment gate. When the event was verified with the platform's own integration secret, only a billing of platformId is considered; the server-wide secret may confirm any platform's billing.
- payment_intent.payment_failed: when the PaymentIntent's metadata.invoice_id names a Transportial subscription invoice of platformId that is still open, raises the "payment failed" billing alert on the subscribed platform (with Stripe's last_payment_error.message) and notifies it per its billing alert settings. A repeat for the same invoice does not notify again.
- Every other event type is acknowledged with 200 and ignored, so Stripe does not retry it.

Idempotency: each PaymentIntent is applied at most once (also when the payment was already confirmed through the public invoice page), so a retried event changes nothing; the applied amount never exceeds the open amount. Confirming a payment method twice has no further effect.

Responds with data holding the JSON text {"received":true}.

Permission: Public, no authentication (authenticity is proven by the Stripe-Signature header).

Errors: 400 when no webhook secret is configured for the platform or server; 401 when the Stripe-Signature header is missing or invalid; 500 when a signed body is not valid JSON.

### Receive a Mollie payment status change

 - [POST /payment/webhook/mollie/{platformId}](https://docs.transportial.com/apis/payment/handle:payment:webhook:mollie.md): Endpoint for Mollie payment webhooks. Point the webhookUrl credential attribute of the platform's Mollie integration (sent when a payment is created) at this URL.

Payload: Mollie posts only the payment id, form-encoded as id=tr_xxx. A JSON body { "id": "tr_xxx" } is accepted as well, for manual replays.

Verification: Mollie webhooks are not signed, so the body is never trusted: the payment is fetched back from the Mollie API (GET /payments/{id}) with the platform's own Mollie API key, and only that answer is used.

Effect: when the fetched payment has status paid and its metadata.invoice_id names an existing invoice of platformId, the payment's amount.value is applied to that invoice; an invoice settled in full becomes PAID, its administration's invoice-paid message automation is sent and a payment activity is logged. Any other status (open, canceled, expired, failed) changes nothing.

Idempotency: each Mollie payment id is applied at most once (capped at the open amount); later calls for the same payment change nothing. Mollie calls the endpoint on every status change and retries non-2xx answers, so the endpoint answers 200 even when the payment is not paid or the fetch-back failed.

Responds with data holding the JSON text {"received":true,"paid":}.

Permission: Public, no authentication (authenticity comes from the fetch-back).

Errors: 400 when no payment id is sent; 404 when the platform has no Mollie integration.

### Receive an Adyen payment notification

 - [POST /payment/webhook/adyen/{platformId}](https://docs.transportial.com/apis/payment/handle:payment:webhook:adyen.md): Endpoint for Adyen standard webhooks (notifications), the authoritative confirmation of payments started through Adyen Sessions for an invoice.

Payload: Adyen's notification envelope { "live": "false", "notificationItems": [ { "NotificationRequestItem": { "eventCode", "success", "pspReference", "originalReference", "merchantAccountCode", "merchantReference", "amount": { "value", "currency" }, "additionalData": { "hmacSignature", "metadata.invoiceId", ... } } } ] }.

Verification: every item must carry additionalData.hmacSignature, the Base64 HMAC-SHA256 (with the hex-decoded hmacKey credential attribute of the platform's Adyen integration) of pspReference:originalReference:merchantAccountCode:merchantReference:amount.value:amount.currency:eventCode:success, compared in constant time. All items are verified before any is applied: when one item has a missing or wrong signature, the request is answered 401 and nothing is applied. A platform without an Adyen integration or hmacKey rejects every webhook.

Effect: an AUTHORISATION item with success "true" pays the invoice it references: additionalData["metadata.invoiceId"], or else the invoice named in merchantReference (Invoice-), only when it belongs to platformId. amount.value (minor units) is applied to the invoice's open amount, capped at that open amount; an invoice settled in full becomes PAID, its administration's invoice-paid message automation is sent and a payment activity is logged. Other event codes are acknowledged and ignored.

Idempotency: each pspReference is applied at most once, so a redelivered notification (also of a partial payment) changes nothing; the applied amount never exceeds the open amount. An AUTHORISATION without a pspReference is ignored.

Responds with Adyen's required acknowledgement { "notificationResponse": "[accepted]" }.

Permission: Public, no authentication (authenticity is proven by the HMAC signature).

Errors: 401 when the platform has no Adyen HMAC key or an item's signature is missing or invalid; 500 when the body is not valid JSON.

## Compliance

Control-tower view of open compliance items across the platform: posting declarations awaiting filing, customs declarations not yet released or discharged, flagged fuel transactions, and unsolved compliance alerts (cabotage limit reached, IMI posting declaration required, certificate expiring soon). Read-only; the items themselves are managed under their own tags. With the `carrierCompliance` feature it also judges whether chauffeurs, vehicles, transport equipment, carriers and capacity contracts are available (required certificates and carrier status) and reports what carriers cost per lane.

### Get the cost history of carriers on a lane

 - [POST /carrierCompliance/laneHistory](https://docs.transportial.com/apis/compliance/get:carrierlanehistory.md): Returns, per carrier, what its forwarded trips on a lane cost within the platform's look-back (laneHistoryMonths, default 12): number of trips, average, lowest and highest expected cost (from the trips' ledger), the average actual cost where one was entered, and the date of the last trip, cheapest average first and per currency. A lane is origin and destination country plus the first lanePostcodeDigits postcode characters (default 2; 0 = country to country) of the first and last stop. Send a tripId or consignmentId to take the lane from it, or the origin and destination yourself. Cancelled and deleted trips do not count.

Permission: get:capacityContract; the platform needs the carrierCompliance feature.

Errors: 400 with carrier_compliance_disabled without the feature, or when the body is not valid JSON; 404 when the trip or consignment is not found on your platform.

### Get the compliance overview

 - [GET /compliance/overview](https://docs.transportial.com/apis/compliance/get:compliance:overview.md): Returns counts of the open compliance items of your platform, for a control-tower overview:

- pendingPostingDeclarations: posting declarations with status required;
- openCustomsDeclarations: customs declarations whose status is not RELEASED or DISCHARGED;
- flaggedFuelTransactions: fuel transactions flagged as anomalous;
- openComplianceAlerts: unsolved alerts raised by the compliance checks (cabotage limit reached, IMI posting declaration required, certificate expiring soon).

Deleted items are not counted. Read-only; a count that cannot be computed is returned as 0.

Permission: get:compliance-overview

### Check whether resources and carriers are available

 - [POST /carrierCompliance:check](https://docs.transportial.com/apis/compliance/check:carriercompliance.md): Judges chauffeurs, vehicles, transport equipment, carriers and capacity contracts against the platform's carrier compliance rules: required certificate types per holder kind (missing, expired at the time of the work, or expiring within the type's warn days) and the carrier status of the business they belong to (the capacity contract's carrier, else their own business). A verdict with a blocking finding means unavailable. Also returns the assignment and forwarding policies, so the client can show a warning or expect a refusal. Read-only: nothing is stored.

Permission: get:trip or get:capacityContract; the platform needs the carrierCompliance feature.

Errors: 400 with carrier_compliance_disabled without the feature, or when the body is not valid JSON.

## CustomsDeclaration

A customs declaration (transit T1/T2, export, import, TIR, ...) linked to a consignment, transport order or trip. It is a structured status record of the declaration and its NCTS data (MRN, customs offices, guarantee reference); the type and status are open codes. Declarations whose status is not RELEASED or DISCHARGED count as open in the compliance overview.

### Cancel a customs declaration

 - [POST /customs-declaration/{id}/cancel](https://docs.transportial.com/apis/customsdeclaration/cancel:customs-declaration.md): Asks customs to invalidate the declaration (NCTS CC014C, AES CC514C, ICS2 IE3Q04) through the integration it was lodged with; reason is sent as the justification. The declaration becomes cancellationRequested until customs decides (cancelled, or back to accepted when refused).

Permission: update:customs-declaration

Errors: 400 customs_no_integration (no enabled customs integration lodges this kind), customs_operation_not_allowed (wrong status or no MRN yet), customs_kind_not_supported, customs_declaration_incomplete (params.missing lists the fields customs requires), customs_integration_misconfigured or customs_gateway_error (the gateway refused or could not be reached); 404 when the declaration does not exist on your platform.

### List customs declarations

 - [POST /customs-declarations/{offset}/{amount}](https://docs.transportial.com/apis/customsdeclaration/search:customs-declarations.md): Returns one page of the non-deleted, non-archived customs declarations of your platform, newest first, plus the total number of matches in total.

- consignmentId: only declarations linked to this consignment.
- status: only declarations with exactly this status.

The body may be empty. A failing query returns an empty page.

Permission: get:customs-declaration

Errors: 400 when the body is not a JSON object or offset/amount is not an integer.

### Get a customs declaration

 - [GET /customs-declaration/{id}](https://docs.transportial.com/apis/customsdeclaration/get:customs-declaration.md): Returns one customs declaration of your platform.

Permission: get:customs-declaration

Errors: 404 when no customs declaration with this id exists on your platform (or it is archived or deleted).

### Update a customs declaration

 - [PUT /customs-declaration/{id}](https://docs.transportial.com/apis/customsdeclaration/update:customs-declaration.md): Updates a customs declaration, for example its status, MRN or release moment. Only the fields present in the body are changed; omitted fields keep their value.

The declaration is identified by the id in the body (the id path parameter is not used to look it up). When no declaration with that id exists yet, a new one is created (upsert); an id that belongs to another platform is rejected.

Permission: update:customs-declaration

Errors: 400 when the body is not valid JSON, a field has the wrong type or validation fails; 404 when the id belongs to a declaration of another platform.

### Delete a customs declaration

 - [DELETE /customs-declaration/{id}](https://docs.transportial.com/apis/customsdeclaration/delete:customs-declaration.md): Permanently deletes a customs declaration. The response contains the declaration as it was before deletion.

Permission: delete:customs-declaration

Errors: 404 when no customs declaration with this id exists on your platform (or it is archived or deleted).

### Amend a filed customs declaration

 - [PUT /customs-declaration/{id}/filing](https://docs.transportial.com/apis/customsdeclaration/update:customs-declaration-filing.md): Sends an amendment of an already filed declaration: a CDS amendment (/amend, for example a changed item price), an updated GMR, an ISF or entry summary replace, or an AES change. Fields not in data are taken from the previous filing where the connector replaces the whole message.

Errors: 400 when the declaration has no authority reference yet (MRN, GMR id, ISF transaction or entry number) or the data is invalid; 404 when the declaration does not exist on your platform.

Permission: update:customs-declaration

### Submit a customs declaration to HMRC or CBP

 - [POST /customs-declaration/{id}/filing](https://docs.transportial.com/apis/customsdeclaration/create:customs-declaration-filing.md): Sends the customs declaration to the authority through the platform's UK or US customs integration: CDS declarations (IM, EX, CO) to HMRC's Customs Declarations API, Goods Movement Records (GMR) to GVMS, and ISF, ISF5, 7501 (entry summary) and EEI filings to CBP ACE through the configured ABI/AES service provider. The declaration moves to SUBMITTED; the authority's later notifications move it further (ACCEPTED, UNDER_CONTROL, RELEASED, REJECTED, CANCELLED, AMENDED).

Goods items not listed in data are taken from the declaration's consignment (cargo items with their HS codes, which are validated for the jurisdiction). An Importer Security Filing is refused for a trip that does not travel by sea.

Errors: 400 when the data does not pass the connector's checks, the declaration type has no UK/US connector, no enabled integration exists or it is not authorised; 404 when the declaration does not exist on your platform.

Permission: update:customs-declaration

### List the customs messages of a declaration

 - [GET /customs-declaration/{id}/messages](https://docs.transportial.com/apis/customsdeclaration/get:customs-declaration-messages.md): Returns the message log of the declaration, oldest first: what was lodged with customs and every answer (acknowledgements, MRN allocation, control, release, rejections with their error pointers), including statuses reported by carrier feeds. Add ?payload=true to include the raw XML.

Permission: get:customs-declaration

Errors: 404 when the declaration does not exist on your platform.

### Amend a customs declaration

 - [POST /customs-declaration/{id}/amend](https://docs.transportial.com/apis/customsdeclaration/amend:customs-declaration.md): Lodges an amendment with the declaration's current data (NCTS CC013C, AES CC513C) through the integration it was lodged with. Allowed while customs has not released it; the declaration becomes amendmentSubmitted until customs accepts (amended) or refuses it (back to accepted).

Permission: update:customs-declaration

Errors: 400 customs_no_integration (no enabled customs integration lodges this kind), customs_operation_not_allowed (wrong status or no MRN yet), customs_kind_not_supported, customs_declaration_incomplete (params.missing lists the fields customs requires), customs_integration_misconfigured or customs_gateway_error (the gateway refused or could not be reached); 404 when the declaration does not exist on your platform.

### Cancel a filed customs declaration

 - [POST /customs-declaration/{id}/filing/cancel](https://docs.transportial.com/apis/customsdeclaration/cancel:customs-declaration-filing.md): Asks the authority to cancel the filing: a CDS cancellation request (invalidation follows as a notification), a GMR delete, an ISF or entry summary delete, or an AES cancel. Pass the reason in reason.

Errors: 400 when the declaration has no authority reference yet; 404 when the declaration does not exist on your platform.

Permission: update:customs-declaration

### List the filings of a customs declaration

 - [GET /customs-declaration/{id}/filings](https://docs.transportial.com/apis/customsdeclaration/get:customs-declaration-filings.md): Returns every transmission of the declaration to HMRC or CBP, newest first, with the authority's correlation id and immediate answer.

Errors: 404 when the declaration does not exist on your platform.

Permission: get:customs-declaration

### Submit a customs declaration

 - [POST /customs-declaration/{id}/submit](https://docs.transportial.com/apis/customsdeclaration/submit:customs-declaration.md): Lodges the declaration with customs through the enabled customs integration that handles its kind (NCTS CC015C or CC007C, AES CC515C, ICS2 IE3F50/IE3F51/IE3F10/IE3N06, ATLAS DETPDD). Allowed from draft and rejected. The declaration becomes submitted and the lodged message is added to its message log; customs answers update it later (MRN, control, release, rejection). A synchronous acknowledgement from the gateway is applied at once.

Permission: update:customs-declaration

Errors: 400 customs_no_integration (no enabled customs integration lodges this kind), customs_operation_not_allowed (wrong status or no MRN yet), customs_kind_not_supported, customs_declaration_incomplete (params.missing lists the fields customs requires), customs_integration_misconfigured or customs_gateway_error (the gateway refused or could not be reached); 404 when the declaration does not exist on your platform.

### Create a customs declaration

 - [POST /customs-declarations](https://docs.transportial.com/apis/customsdeclaration/create:customs-declaration.md): Creates a customs declaration (transit T1/T2, export, import, TIR, ...) for a consignment, transport order or trip. It is a structured status record of the declaration and its NCTS data (MRN, customs offices, guarantee); the declaration itself is not submitted to customs by this endpoint.

Declarations whose status is not RELEASED or DISCHARGED count as open in the compliance overview.

Permission: create:customs-declaration

Errors: 400 when the body is not valid JSON or a field has the wrong type.

## PostingDeclaration

An IMI posting-of-workers declaration (EU Mobility Package) for a driver operating in a host country where the operator has no establishment. Declarations are created automatically when a completed move brings a trip's driver into such a country (driven by the platform's posting rules), or manually. They move from required to declared (with the IMI reference), expired or dismissed (false positive); required ones count as pending in the compliance overview.

### Create a posting declaration

 - [POST /posting-declarations](https://docs.transportial.com/apis/postingdeclaration/create:posting-declaration.md): Manually creates an IMI posting-of-workers declaration (EU Mobility Package) for a driver operating in a host country. Declarations are normally created automatically when a completed move brings a trip's driver into a country where the operator has no establishment; use this endpoint for postings the detection missed. status defaults to required.

Permission: create:posting-declaration

Errors: 400 when the body is not valid JSON, a field has the wrong type or status is not a known value.

### Get a posting declaration

 - [GET /posting-declaration/{id}](https://docs.transportial.com/apis/postingdeclaration/get:posting-declaration.md): Returns one IMI posting-of-workers declaration of your platform.

Permission: get:posting-declaration

Errors: 404 when no posting declaration with this id exists on your platform (or it is archived or deleted).

### List posting declarations

 - [POST /posting-declarations/{offset}/{amount}](https://docs.transportial.com/apis/postingdeclaration/search:posting-declarations.md): Returns one page of the non-deleted, non-archived posting declarations of your platform, newest first, plus the total number of matches in total.

- status: only declarations with this status (case-insensitive; unknown values are ignored).
- chauffeurId: only declarations of this driver.

The body may be empty. A failing query returns an empty page.

Permission: get:posting-declaration

Errors: 400 when the body is not a JSON object or offset/amount is not an integer.

### Mark a posting declaration as filed

 - [PUT /posting-declaration/{id}/declare](https://docs.transportial.com/apis/postingdeclaration/declare:posting-declaration.md): Records that the declaration has been filed in the IMI portal: sets status to declared, declaredAt to now and, when given, declarationReference to the reference from the body. Works from any current status.

Permission: update:posting-declaration

Errors: 400 when the body is not empty and not a JSON object; 404 when no posting declaration with this id exists on your platform (or it is archived or deleted).

### Dismiss a posting declaration

 - [PUT /posting-declaration/{id}/dismiss](https://docs.transportial.com/apis/postingdeclaration/dismiss:posting-declaration.md): Flags a detected posting as not actually requiring a declaration (false positive) by setting status to dismissed. Dismissed declarations no longer count as pending in the compliance overview. The request body is ignored. The related "IMI posting declaration required" alert is not solved automatically.

Permission: update:posting-declaration

Errors: 404 when no posting declaration with this id exists on your platform (or it is archived or deleted).

## Note

Internal notes on a transport order, business, vehicle, chauffeur, user, certificate, tire or ban, added, changed or removed one at a time instead of through a full entity update. Notes are also returned (and can be replaced as a whole) on the entity itself. Changing a note needs the `update:` permission of the entity it belongs to; notes on users need a platform administrator. The server records the author. Users @mentioned in a new or changed note on a transport order, business or vehicle are notified.

### Change a note of an entity

 - [PUT /note/{entityType}/{entityId}/{noteId}](https://docs.transportial.com/apis/note/update:note.md): Replaces the title and content of one note and saves the entity. The author stays the user who wrote the note. Returns every note of the entity after the change.

Errors: 400 when the note has neither a title nor content, or the title is longer than 255 or the content longer than 100000 characters; 404 when the entity type is unknown, no such entity exists on your platform, or the entity has no note with this id.

Permission: update: permission of the entity type, for example update:business; notes on users also need a platform administrator (role level 1000 or higher).

### Remove a note from an entity

 - [DELETE /note/{entityType}/{entityId}/{noteId}](https://docs.transportial.com/apis/note/delete:note.md): Removes one note from the entity and saves the entity. Returns the remaining notes.

Errors: 404 when the entity type is unknown, no such entity exists on your platform, or the entity has no note with this id.

Permission: update: permission of the entity type, for example update:business; notes on users also need a platform administrator (role level 1000 or higher).

### Add a note to an entity

 - [POST /note/{entityType}/{entityId}](https://docs.transportial.com/apis/note/create:note.md): Adds a note to the entity and saves the entity. The signed-in user is recorded as the author. Returns every note of the entity after the change.

Errors: 400 when the note has neither a title nor content, or the title is longer than 255 or the content longer than 100000 characters; 404 when the entity type is unknown or no such entity exists on your platform.

Permission: update: permission of the entity type, for example update:business; notes on users also need a platform administrator (role level 1000 or higher).

## Chat

Conversations between platform users, businesses, chauffeurs and vehicles (board computers). A chat is PRIVATE (members only) or PUBLIC (readable and writable by the whole platform); consignment and vehicle chats are created automatically as PUBLIC entity chats. Each member has a cumulative permission (READ, CHAT, ADMIN) and a personal read anchor that drives unread counts and the unread line; platforms can share read state for entity chats. Messages carry read/received receipts, can be auto-translated, and can flow to and from e-mail, WhatsApp, SMS and Telegram through external channel bindings.

### List messages of a chat (legacy path)

 - [GET /chat/message/{id}](https://docs.transportial.com/apis/chat/get:chat:message.md): Legacy path for the message listing. The handler treats id as a chat id and runs the paged
message listing of GET /chat/messages/{id}/{offset}/{amount}; because this path has no offset and
amount segments the request currently fails with a server error. Use the paged path instead.

Permission: Any authenticated user (no specific permission).

Errors: 404 when no chat with this id exists or you are not a member of this private chat.

### Delete a message

 - [DELETE /chat/message/{id}](https://docs.transportial.com/apis/chat/delete:chat:message.md): Deletes a message. The author of the message or an ADMIN member of its chat may delete it.

Permission: Any authenticated user (no specific permission).

Errors: 404 when no message with this id exists; 401 when you are neither the author nor an ADMIN
member.

### Mark a message as read

 - [GET /chat/message/{id}/read](https://docs.transportial.com/apis/chat/read:chat:message.md): Records a read receipt for you on the message and clears your unread state for its chat (your read
anchor moves to the newest message). With shared read state for entity chats, the receipt and the reset are
applied to every member.

Note: the handler checks for a path parameter named {id} instead of id, so it currently answers 422
with success: false; use POST /chat/messages/read instead.

Permission: Any authenticated user (no specific permission).

Errors: 404 when no message with this id exists or you are not a member of its private chat; 422 as
described above.

### Update an external channel binding

 - [PUT /chat/{chatId}/integrations/{bindingId}](https://docs.transportial.com/apis/chat/update:chat:integration.md): Changes the display name and the inbound/outbound switches of an external channel binding of the
chat. Body fields displayName, enableInbound and enableOutbound are optional; omitted fields keep their
value. The channel type and identifier cannot be changed; delete and re-create the binding instead.

The response is { "success": true, "binding": {...} } with id, channelType (email, whatsapp, telegram, sms, ...), externalIdentifier (the
e-mail address, phone number or chat id on the external channel), displayName, enableInbound and
enableOutbound.

Only ADMIN members of the chat may update bindings, and the binding must belong to this chat.

Permission: put:chat

Errors: 404 when the chat or the binding does not exist; 401 when you are not an ADMIN member or the
binding belongs to another chat.

### Remove an external channel binding

 - [DELETE /chat/{chatId}/integrations/{bindingId}](https://docs.transportial.com/apis/chat/delete:chat:integration.md): Deletes an external channel binding of the chat; messages from that identifier no longer arrive in
this chat. The response is { "success": true }.

Only ADMIN members of the chat may remove bindings, and the binding must belong to this chat.

Permission: delete:chat

Errors: 404 when the chat or the binding does not exist; 401 when you are not an ADMIN member or the
binding belongs to another chat.

### Get a chat

 - [GET /chat/{id}](https://docs.transportial.com/apis/chat/get:chat.md): Returns one chat with its members, latest message, identifiers and derived title and image.

Members with at least READ permission and everyone on a PUBLIC chat may read it. A portal user who is not a
member but whose business owns the chat's consignment (customer or, for suppliers, executing business) is
added as a CHAT member on first access. Portal users never see consignment chats of other businesses.

Permission: get:chat

Errors: 404 when no chat with this id exists or a portal user may not see it; 401 when you are not
allowed to read it.

### Delete a chat

 - [DELETE /chat/{id}](https://docs.transportial.com/apis/chat/delete:chat.md): Deletes a chat. Only a member with ADMIN permission may delete it.

Permission: delete:chat

Errors: 404 when no chat with this id exists or you are not a member of it; 401 when you are a member
without ADMIN permission.

### Mark a chat as unread

 - [GET /chat/{id}/unread](https://docs.transportial.com/apis/chat/unread:chat.md): Marks the chat as unread for you: sets markedAsUnread, raises your unread counter to at least 1 and
moves your read anchor to just before the newest message, so that message shows below the unread line
again. The chat's own unread counter is raised to at least 1 as well.

When the platform shares read state for entity chats, every member and the chat's shared anchor are marked
unread instead.

Permission: get:chat, and you must be able to read the chat: a member with at least READ permission,
or any user of the platform for a PUBLIC chat (portal users only for chats of their own consignments).

Errors: 404 when no chat with this id exists or you cannot read it.

### List chats

 - [GET /chats/{offset}/{amount}](https://docs.transportial.com/apis/chat/get:chats.md): Returns one page of the chats visible to you, most recently active first (by the time of the newest
message, falling back to the chat's last update), plus the total number of visible chats.

Visibility: you see the chats you are a member of plus every PUBLIC chat of your platform (the chats
created automatically for consignments and vehicles are PUBLIC). Portal (customer or supplier) users only
see consignment chats whose transport order belongs to one of their businesses.

Every chat carries its display title and image: for a private chat with at most two members they are
derived from the other member (user, business, chauffeur or vehicle). unreadMessages per chat is the number
of messages after your read anchor (or the chat's shared anchor when the platform shares read state for entity
chats); messagesUnread is the sum over all chats matching the filter. Use includeSystemMessages to control
whether board-computer status messages count.

Permission: get:chats

Errors: 400 when offset is not a number.

### Search chats

 - [POST /chats/{offset}/{amount}](https://docs.transportial.com/apis/chat/search:chats.md): Full-text search over the chats visible to you, newest update first.

The body is a JSON object with an optional query (string) and an optional filter (direct, group,
vehicle or consignment, same meaning as on GET /chats/{offset}/{amount}; when absent the filter query
parameter is used). The query is split into alphanumeric terms and every term must occur (substring match) in
the chat title or in a member's username, email, first name, last name or phone.

When the body has no query key the search is skipped and the first 20 chats of the regular listing are
returned, ignoring offset and amount. When a filter is given, or the caller is a portal user, the filter
is applied to the first 500 hits only. messagesUnread is not computed here and is always 0.

Visibility: you see the chats you are a member of plus every PUBLIC chat of your platform (the chats
created automatically for consignments and vehicles are PUBLIC). Portal (customer or supplier) users only
see consignment chats whose transport order belongs to one of their businesses.

Permission: get:chats

Errors: 400 when the body is not a JSON object or offset is not a number.

### Send a chat message to an external channel

 - [POST /chat/{chatId}/send-external](https://docs.transportial.com/apis/chat/send:chat:external.md): Posts a message in the chat and delivers it through one external channel binding.

Body fields (all required): channelType (EMAIL, WHATSAPP, SMS or TELEGRAM, matched by enum name),
identifier (the external identifier of an existing binding for that channel type) and message (HTML is
sanitised to basic formatting tags and links). Optional attachments is an array of file ids (uploaded files
or consignment documents of your platform; unknown ids are skipped) that are added to the chat message and sent
as e-mail attachments. The message is saved in the chat first; it is then sent via the binding's integration.

The response is { "success": true, "message": {...} } with the saved message. When delivery fails (no
binding for the identifier, unsupported channel, provider error) the message stays in the chat and the
response is HTTP 200 with { "success": false, "error": "" }.

You must be a member of the chat with at least CHAT permission.

Permission: post:chat

Errors: 400 when a field is missing or channelType is not a known channel; 404 when the chat does not
exist or you are not a member; 401 when your membership lacks CHAT permission.

### List external channels of a chat

 - [GET /chat/{chatId}/integrations](https://docs.transportial.com/apis/chat/get:chat:integrations.md): Lists the external channel bindings of a chat: the e-mail addresses, WhatsApp/SMS numbers or Telegram
chats whose messages flow into (and out of) this chat through a configured integration.

The response is { "success": true, "bindings": [...] }; each binding has id, channelType (email, whatsapp, telegram, sms, ...), externalIdentifier (the
e-mail address, phone number or chat id on the external channel), displayName, enableInbound and
enableOutbound, plus
integration with its id and name.

Members with at least READ permission and everyone on a PUBLIC chat may list them.

Permission: get:chat

Errors: 404 when no chat with this id exists; 401 when you may not read the chat.

### Bind an external channel to a chat

 - [POST /chat/{chatId}/integrations](https://docs.transportial.com/apis/chat/create:chat:integration.md): Connects an external channel identifier to the chat so inbound messages from it land in this chat
and outbound messages can be sent to it.

Body fields: integrationId (required, a manual integration of your platform), channelType (required, one
of EMAIL, CHAT, NOTIFICATION, WHATSAPP, TELEGRAM, SMS; matched by enum name), externalIdentifier
(required, e-mail address, phone number or external chat id), displayName (defaults to the identifier),
enableInbound and enableOutbound (both default to true). When a binding for the same channel type and
identifier already exists anywhere, it is moved to this chat and overwritten instead of creating a duplicate.

The response is { "success": true, "binding": {...} } with id, channelType (email, whatsapp, telegram, sms, ...), externalIdentifier (the
e-mail address, phone number or chat id on the external channel), displayName, enableInbound and
enableOutbound.

Only ADMIN members of the chat may bind channels.

Permission: post:chat

Errors: 400 when a required field is missing or channelType is not a known channel; 404 when the
chat or the integration does not exist; 401 when you are not an ADMIN member.

### Update a chat

 - [PUT /chat](https://docs.transportial.com/apis/chat/update:chat.md): Updates the title, type, image and members of an existing chat identified by id in the body.

Only the keys present are applied: title; type (PRIVATE/PUBLIC, invalid values are ignored); image
as { "id": "" }. The users array is reconciled against the current members: each entry names a
user as { "user": { "id": "" } } or { "user": "" } with an optional permission
(defaults to CHAT). Listed members keep their membership with the given permission, new users are added,
and members missing from the list are removed (soft-deleted). You are never removed and are re-added as
ADMIN when absent. Omitting users leaves the members unchanged.

Permission: update:chat, and you must be an ADMIN member of the chat. Platform administrators (role level
1000 and up) may also update chats they do not administer, such as vehicle and consignment chats.

Errors: 400 when the body is not valid JSON or has no id; 401 when you are not an ADMIN of the chat;
404 when no chat with this id exists (or it is not visible to you as a portal user).

### Create a chat

 - [POST /chat](https://docs.transportial.com/apis/chat/create:chat.md): Starts a private conversation between you and the given users.

You are added as ADMIN member, every listed user as CHAT member; unknown user ids are skipped. When exactly one
user is listed and you already have a one-to-one private chat with that user (not attached to a vehicle or
consignment, neither member removed), that chat is returned instead of creating a second one and existing is
true. The nonce of the request is echoed on the returned chat.

Permission: create:chat

Errors: 400 when the body is not valid JSON or has no users array.

### Edit a message

 - [PUT /chat/message](https://docs.transportial.com/apis/chat/update:chat:message.md): Replaces the text of a message and marks it as edited. Only the message field of the body is read
(HTML is sanitised like on create); the author or an ADMIN member of the chat may edit.

The handler reads the message id from an id path segment, which this path does not have, so the request
currently fails with a server error.

Permission: update:chat

Errors: 404 when no message with this id exists; 401 when you are neither the author nor an ADMIN
member.

### Send a message in a chat

 - [POST /chat/message](https://docs.transportial.com/apis/chat/create:chat:message.md): Posts a new message in a chat.

Body fields: chat (required, chat id), message (required, text; HTML is sanitised to b, i, u, br,
div, span, strong, italic and links), state (optional, enum name NORMAL, IMPORTANT, PINNED or
DELETED; any other value falls back to NORMAL), files (optional, array of ids of uploaded files to attach), nonce (optional, echoed
back), sendAsEmail (optional boolean) and replyToMailMessageId (optional mail message id).

The message is marked read and received for you, counts as unread for every other member and becomes the
chat's latest message. Members with CHAT permission and everyone on a PUBLIC chat may post. Integrations
listening to AfterCreatingMessage are queued. With sendAsEmail the message is also sent as an e-mail
reply to the given mail message, or to the latest inbound e-mail of the chat; nothing is mailed when there is
none.

Permission: create:chat

Errors: 400 when the body is not valid JSON or message is missing; 404 when no chat with this id
exists or you are not a member of this private chat; 401 when your membership lacks CHAT permission.

### Mark messages as received

 - [POST /chat/messages/received](https://docs.transportial.com/apis/chat/received:chat:messages.md): Records a received (delivered) receipt for you on each of the given messages. Unknown ids are skipped;
the messages may belong to different chats. Returns the messages that were found; totalResults is their
number.

Permission: Any authenticated user (no specific permission).

Errors: 400 when the body is not valid JSON; 404 when one of the messages is in a private chat you
are not a member of.

### List messages across all chats

 - [GET /chats/messages/{offset}/{amount}](https://docs.transportial.com/apis/chat/get:chats:messages.md): Returns the message feed: every message of every chat visible to you (your chats plus the PUBLIC
chats of your platform; portal users only see consignment chats of their own businesses), newest first. Each
message carries its chat with the derived title and image.

This feed is read-only: no read or received receipts are recorded. totalResults is a paging watermark, not
a total count: offset plus the number of returned messages, plus one when another page exists. Keep paging
while totalResults is greater than offset plus page size. unreadResults is always 0.

Permission: Any authenticated user (no specific permission).

Errors: 400 when offset is not a number.

### Mark messages as read

 - [POST /chat/messages/read](https://docs.transportial.com/apis/chat/read:chat:messages.md): Records read receipts for you on the given messages (all expected to belong to the same chat, the chat
of the first message decides your membership) and clears your unread state for that chat: unread counters
and markedAsUnread are reset and your read anchor moves to the newest message. With shared read state for
entity chats, receipts and the reset are applied to every member. Unknown ids are skipped.

Returns only the messages that did not have your read receipt yet; totalResults is their number.

Permission: Any authenticated user (no specific permission).

Errors: 400 when the body is not valid JSON; 404 when none of the ids is a message or you are not a
member of its private chat.

### List messages of a chat

 - [GET /chat/messages/{id}/{offset}/{amount}](https://docs.transportial.com/apis/chat/get:chat:messages.md): Returns one page of the messages of a chat plus the total number of messages.

Messages are ordered oldest to newest within the page; the page itself is taken from the newest end
(offset 0 is the most recent page). Opening the listing records a received receipt for every returned
message. Members with at least READ permission and everyone on a PUBLIC chat may read; on a PUBLIC chat a
non-member (except portal users) becomes a member anchored at the newest message, unless the platform shares
read state for entity chats.

unreadResults and unreadFromMessageId describe the unread line: the number of messages after your read
anchor and the id of the oldest of them (null when nothing is unread). When the platform auto-translates
messages, translatedMessage is filled in your language for messages written by others.

Permission: Any authenticated user (no specific permission).

Errors: 400 when offset is not a number; 404 when no chat with this id exists or you are not a
member of this private chat; 401 when your membership lacks READ permission.

### Search messages of a chat

 - [POST /chat/messages/{id}/{offset}/{amount}](https://docs.transportial.com/apis/chat/search:chat:messages.md): Intended for searching the messages of a chat. The request body is currently not read: the handler
only searches when the path carries a query segment, which this path does not have, so the result is the
same page as GET /chat/messages/{id}/{offset}/{amount}.

Messages are ordered oldest to newest within the page; the page itself is taken from the newest end
(offset 0 is the most recent page). Opening the listing records a received receipt for every returned
message. Members with at least READ permission and everyone on a PUBLIC chat may read; on a PUBLIC chat a
non-member (except portal users) becomes a member anchored at the newest message, unless the platform shares
read state for entity chats.

unreadResults and unreadFromMessageId describe the unread line: the number of messages after your read
anchor and the id of the oldest of them (null when nothing is unread). When the platform auto-translates
messages, translatedMessage is filled in your language for messages written by others.

Permission: Any authenticated user (no specific permission).

Errors: 400 when offset is not a number; 404 when no chat with this id exists or you are not a
member of this private chat; 401 when your membership lacks READ permission.

### Mark a message as received

 - [GET /chat/message/{id}/received](https://docs.transportial.com/apis/chat/received:chat:message.md): Records a received (delivered) receipt for you on a message.

The handler ignores the id path segment and reads the message id from a JSON request body
{ "message": "" }; a GET without that body fails with 400. Prefer
POST /chat/messages/received.

Permission: Any authenticated user (no specific permission).

Errors: 400 when the body is missing or has no message; 404 when no message with this id exists or
you are not a member of its private chat.

### Mark a chat as read

 - [GET /chat/{id}/read](https://docs.transportial.com/apis/chat/read:chat.md): Clears your unread state for the whole chat: markedAsUnread and the unread counters are reset and
your read anchor moves to the newest message, so no unread line is shown any more. Use it after
GET /chat/{id}/unread, when every message already has your read receipt.

When the platform shares read state for entity chats (consignment, vehicle or chauffeur chats) and you are
not a portal user, the read applies to every member and to the chat's shared anchor instead, even without
a membership.

Permission: get:chat

Errors: 404 when no chat with this id exists, or you are not a member of a private chat.

### Translate a message

 - [GET /chat/message:translate/{id}](https://docs.transportial.com/apis/chat/translate:chat:message.md): Translates the message text into your language (your user language, English when none is set) using
the platform's translation provider and returns the message with translatedMessage filled. The stored
message text is not changed.

Permission: Any authenticated user (no specific permission).

Errors: 404 when no message with this id exists.

## MessageTemplate

Reusable chat message texts of a platform. A template's text is rendered by the message automation template interpreter against the chat's vehicle, its current trip or its upcoming trips (contextEntity), producing text a dispatcher can insert into the chat composer. Rendering never posts a message.

### Update a message template

 - [PUT /messageTemplate](https://docs.transportial.com/apis/messagetemplate/update:messagetemplate.md): Updates an existing message template identified by id in the body; the fields in the body are
merged into the stored template.

Permission: update:messageTemplate or create:messageTemplate

Errors: 400 when the body is not valid JSON; 404 when no template with this id exists.

### Create a message template

 - [POST /messageTemplate](https://docs.transportial.com/apis/messagetemplate/create:messagetemplate.md): Creates a reusable message template. The message is template text that is rendered against the
chat's vehicle, the vehicle's current trip or its upcoming trips, depending on contextEntity (see
GET /messageTemplate/{id}/message/{chat}).

Permission: create:messageTemplate

Errors: 400 when the body is not valid JSON or contains an unknown enum value.

### List message templates

 - [GET /messageTemplates/{offset}/{amount}](https://docs.transportial.com/apis/messagetemplate/get:messagetemplates.md): Returns one page of the message templates of your platform plus the total number of templates.

Permission: get:messageTemplates

Errors: 400 when offset is not a number.

### Search message templates

 - [POST /messageTemplates/{offset}/{amount}](https://docs.transportial.com/apis/messagetemplate/search:messagetemplates.md): Searches the active (not archived, not deleted) message templates of your platform, most recently
updated first, and returns one page plus the total number of matches.

The body is a JSON object with optional fields: query (case-insensitive substring match on the template's
search string; ignored when it contains no letters or digits) and language (id of a language; only
templates in that language are returned).

amount is not clamped on this endpoint: the given value is used as page size (default 20).

Permission: get:messageTemplate

Errors: 400 when the body is not a JSON object or offset/amount is not a number.

### Get a message template

 - [GET /messageTemplate/{id}](https://docs.transportial.com/apis/messagetemplate/get:messagetemplate.md): Returns one message template of your platform.

Permission: get:messageTemplate

Errors: 404 when no template with this id exists.

### Delete a message template

 - [DELETE /messageTemplate/{id}](https://docs.transportial.com/apis/messagetemplate/delete:messagetemplate.md): Deletes a message template of your platform.

Permission: delete:messageTemplate

Errors: 404 when no template with this id exists.

### Render a message template for a chat

 - [GET /messageTemplate/{id}/message/{chat}](https://docs.transportial.com/apis/messagetemplate/get:messagefrommessagetemplate.md): Renders a message template against a chat and returns the resulting text in generatedMessage. Nothing
is posted; the client can put the text in the composer.

The template's contextEntity decides the data the template is rendered with: vehicle uses the chat's
vehicle, trip the vehicle's current trip, upcoming_trips the first 10 active trips of the vehicle. When the
chat has no vehicle (or the vehicle no trip) the template is rendered against an empty object.

Permission: get:messageFromMessageTemplate

Errors: 404 when the template or the chat does not exist, or when contextEntity is upcoming_trips
and the chat has no vehicle.

## Message automation

Rules that send a message, a question or a file request over e-mail, in-app notification, driver chat, WhatsApp, Telegram or SMS when an entity event occurs (e.g. AfterUpdatingTrip) or on a schedule, optionally over TQL query results. Filters decide whether a run sends anything; recipients are static or picked from the trigger data, and localized templates are filled with that data. Each automation gets a server-managed integration, every run is logged as a task with its recipients and their answers or files, and users can opt in to user-enableable automations. A global template catalogue and AI generation help create new automations.

### List message automations

 - [GET /messageAutomations/{offset}/{amount}](https://docs.transportial.com/apis/message-automation/get:messageautomations.md): Returns one page of the message automations of your platform (templates excluded, non-archived), most recently updated first, plus the total number in totalResults.

Permission: get:messageAutomations

Errors: 400 when offset is not an integer.

### Search message automations

 - [POST /messageAutomations/{offset}/{amount}](https://docs.transportial.com/apis/message-automation/search:messageautomations.md): Full-text search over the non-deleted, non-archived message automations of your platform. Returns one page of matches plus the total number of matches in totalResults.

- query: matched as a phrase against name, description, type, template category, query and note names.
- template: when true, searches the global template catalogue instead of your platform.
- sortBy: sort clauses on sortable index fields; without it results are ordered by relevance.

A failing search returns an empty page instead of an error.

Permission: search:messageAutomations

Errors: 400 when the body is not valid JSON or offset is not an integer.

### Update a message automation

 - [PUT /messageAutomation](https://docs.transportial.com/apis/message-automation/update:messageautomation.md): Updates the message automation identified by the id in the body; fields left out keep their value. Side effects:

- the automation's integration is created when missing, otherwise its enabled state, schedule, trigger type and trigger event are synchronised with the automation;
- runs of the automation that are still queued or processing are marked as error;
- when automationConfig.triggerType is trigger and there is no schedule, a default schedule is set;
- for file request automations, the files in filesRequested are deleted;
- with template: true the automation is stored as a disabled catalogue template without platform or integration.

Catalogue templates are shared by every platform, so writing one (sending template: true, or any update of an id that is stored as a template) is reserved for super administrators. All other updates only reach automations of your own platform.

A body id that is unknown on every platform creates a new automation.

Permission: update:messageAutomation (template writes additionally require a super administrator)

Errors: 400 when the body is not valid JSON, does not match the schema, or contains an invalid cron expression; 401 when a user who is not a super administrator writes a template; 404 when the id belongs to an automation of another platform.

### Create a message automation

 - [POST /messageAutomation](https://docs.transportial.com/apis/message-automation/create:messageautomation.md): Creates a message automation on your platform together with the integration that schedules it or listens for its trigger event. When automationConfig.triggerType is trigger and no schedule is sent, a default schedule is set. Saving emits the AfterCreatingMessageAutomation event.

With template: true a catalogue template is created instead: it is stored without platform and disabled, gets no integration, and is offered to every platform. Only super administrators can create templates.

Permission: create:messageAutomation (templates additionally require a super administrator)

Errors: 400 when the body is not valid JSON, does not match the schema, or contains an invalid cron expression; 401 when a non super administrator sends template: true.

### List the runs of a message automation

 - [GET /messageAutomation/{id}/tasks/{offset}/{amount}](https://docs.transportial.com/apis/message-automation/get:messageautomation:tasks.md): Returns one page of the runs (tasks) of a message automation of your platform, most recently updated first, with their status and the messages and recipients of each run, plus the total number of runs in totalResults.

Permission: get:messageAutomationTasks

Errors: 400 when offset is not an integer; 404 when no non-archived message automation with this id exists on your platform.

### Get a message automation

 - [GET /messageAutomation/{id}](https://docs.transportial.com/apis/message-automation/get:messageautomation.md): Returns a single message automation of your platform, including its configuration and integration.

Permission: get:messageAutomation

Errors: 404 when no non-archived message automation with this id exists on your platform.

### Delete a message automation

 - [DELETE /messageAutomation/{id}](https://docs.transportial.com/apis/message-automation/delete:messageautomation.md): Permanently deletes a message automation of your platform, together with its integration, and returns it as it was before deletion.

Permission: delete:messageAutomation

Errors: 404 when no non-archived message automation with this id exists on your platform.

### Duplicate a message automation

 - [POST /messageAutomation/{id}/duplicate](https://docs.transportial.com/apis/message-automation/duplicate:messageautomation.md): Saves a copy of a message automation of your platform. The copy gets a new id and leaves out the notes, the template flag, the integration and the connector (automationConfig.manualIntegration). Unlike POST /messageAutomation, no integration is created for the copy; it is created on the first update.

Permission: duplicate:messageAutomation

Errors: 404 when no non-archived message automation with this id exists on your platform.

### List your automation subscriptions

 - [GET /messageAutomation/user-preferences](https://docs.transportial.com/apis/message-automation/get:messageautomation:userpreferences.md): Returns every user-enableable message automation of your platform (automationConfig.userEnableable) with whether the authenticated user has opted in. Users without a stored preference are not subscribed (enabled: false). The list is not paginated.

Permission: any authenticated user.

### Subscribe to or unsubscribe from an automation

 - [POST /messageAutomation/user-preference](https://docs.transportial.com/apis/message-automation/toggle:messageautomation:userpreference.md): Stores whether the authenticated user wants to receive the messages of a user-enableable message automation. Subscribed users that pass the automation's userPermissionFilters receive its messages in addition to the configured recipients.

Permission: any authenticated user.

Errors: 400 when the body is not valid JSON; 404 when the automation does not exist on your platform or is not user-enableable.

### List the template catalogue

 - [GET /messageAutomation-templates](https://docs.transportial.com/apis/message-automation/get:messageautomationtemplates.md): Returns every non-deleted message automation template of the global catalogue. The list is not paginated. Use POST /messageAutomation-template/{id}/duplicate to load one into your platform.

Permission: get:messageAutomationTemplates

### Generate a message automation with AI

 - [POST /messageAutomation/generate](https://docs.transportial.com/apis/message-automation/generate:messageautomation.md): Proposes a message automation configuration from a free-text instruction and/or uploaded documents, using the AI model configured for your platform. The proposal is returned as JSON and is not saved; review it and save it with POST /messageAutomation. File ids in documents that are unknown, deleted or owned by another platform are skipped.

Permission: create:messageAutomation

Errors: 400 when the body does not match the request schema.

### Copy a catalogue template

 - [POST /messageAutomation-template/{id}/duplicate](https://docs.transportial.com/apis/message-automation/duplicate:messageautomationtemplate.md): Returns a copy of a message automation template from the global catalogue, ready to pre-fill a create form. The copy has no id, notes, integration or connector, and the Transportial branding placeholders in the template (logo, colours, host name) are replaced with your platform's branding. The copy is not saved; save it with POST /messageAutomation.

Permission: duplicate:messageAutomation

Errors: 404 when the id does not belong to a non-deleted, non-archived catalogue template.

## Notification

Messages addressed to a single user of the platform, shown in their notification list and optionally pushed to their phone or e-mailed. Listing your notifications records received receipts; read and received endpoints add receipts per call. Only the addressee or an administrator (role level 100 or higher) may update or delete a notification.

### Mark notifications as read

 - [POST /notification/read](https://docs.transportial.com/apis/notification/read:notifications.md): Adds a read receipt for you to each of the given notifications (one per call, also when already read).
Unknown ids and notifications addressed to someone else are skipped (administrators, role level 100 and
up, may mark any notification of the platform). Returns the notifications that were found; totalResults is their number.

Permission: Any authenticated user (no specific permission).

Errors: 400 when the body is not valid JSON.

### Update a notification

 - [PUT /notification](https://docs.transportial.com/apis/notification/update:notification.md): Replaces the title, link, message and image (file id) of the notification identified by id in
the body; all four are required and other fields are ignored. Only the addressee of the notification or a user
with role level 100 or higher may update it. Also available as PATCH /notification with the same body.

Permission: update:notification

Errors: 400 when the body is not valid JSON or id, title, link, message or image is missing;
404 when no notification with this id exists; 401 when you are not the addressee or an administrator.

### Create a notification

 - [POST /notification](https://docs.transportial.com/apis/notification/create:notification.md): Creates a notification addressed to yourself, with the given title, link, message and image
(id of an uploaded file; an unknown id leaves the notification without image). All other fields of the body
are ignored and keep their defaults; no push or e-mail is sent by this endpoint.

Permission: create:notification

Errors: 400 when the body is not valid JSON or title, link, message or image is missing or not a
string.

### Get a notification

 - [GET /notification/{id}](https://docs.transportial.com/apis/notification/get:notification.md): Returns one notification of your platform with its read receipts. Only the addressee of the
notification and administrators (role level 100 and up) can read it.

Permission: get:notification

Errors: 404 when no notification with this id exists or it is addressed to someone else.

### Delete a notification

 - [DELETE /notification/{id}](https://docs.transportial.com/apis/notification/delete:notification.md): Deletes a notification. Only its addressee or a user with role level 100 or higher may delete it.

Permission: delete:notification

Errors: 404 when no notification with this id exists; 401 when you are not the addressee or an
administrator.

### Mark a notification as received

 - [GET /notification/{id}/received](https://docs.transportial.com/apis/notification/received:notification.md): Adds a received (delivered) receipt for you to the notification and returns it. A receipt is added on
every call. When no notification with this id exists, or it is addressed to someone else (and you are not
an administrator, role level 100 and up), nothing is changed and the response is still success: true with
notification null.

Permission: Any authenticated user (no specific permission).

### List your notifications

 - [GET /notifications/{offset}/{amount}](https://docs.transportial.com/apis/notification/get:notifications.md): Returns one page of your own notifications on this platform, newest first, plus the total number and
the number you have not read yet (notificationsUnread).

Side effect: every returned notification you had not received yet gets a received receipt for you.

Permission: get:notifications

Errors: 400 when offset is not a number.

### Mark a notification as read

 - [GET /notification/{id}/read](https://docs.transportial.com/apis/notification/read:notification.md): Adds a read receipt for you to the notification and returns it. A receipt is added on every call, also
when you had read it before. When no notification with this id exists, or it is addressed to someone else
(and you are not an administrator, role level 100 and up), nothing is changed and the response is still
success: true with notification null.

Permission: Any authenticated user (no specific permission).

### Mark notifications as received

 - [POST /notification/received](https://docs.transportial.com/apis/notification/received:notifications.md): Adds a received (delivered) receipt for you to each of the given notifications (one per call). Unknown
ids and notifications addressed to someone else are skipped (administrators, role level 100 and up, may mark
any notification of the platform). Returns the notifications that were found; totalResults is their number.

Permission: received:notifications

Errors: 400 when the body is not valid JSON.

## Alert

Alerts are detected problems or points of attention: failing or stalled integrations, and trip, vehicle, driver or consignment conditions. Each has a severity, explanation, suggested action, related entities and an SLA due time taken from the platform's alert settings. An alert stays open until it is solved or ignored, by a user or automatically when the condition clears, and it re-opens when the condition returns. The open-alert lists register the caller's view so changes are pushed live.

### Ignore an alert

 - [PUT /alert/{id}/ignore](https://docs.transportial.com/apis/alert/ignore:alert.md): Marks an alert as ignored (triaged, not fixed): sets ignored, ignoredAt (now) and ignoredBy (the calling user), saves it and pushes the change to every open view, where the alert disappears from the open-alert lists. Calling it on an alert that is already ignored changes nothing. No request body.

Permission: ignore:alert

Errors: 404 when no alert with this id exists on your platform.

### Solve an alert

 - [PUT /alert/{id}/solve](https://docs.transportial.com/apis/alert/solve:alert.md): Marks an alert as solved: sets solved, solvedAt (now) and solvedBy (the calling user), saves it and pushes the change to every open view, where the alert disappears from the open-alert lists. Calling it on an alert that is already solved changes nothing. No request body.

Permission: solve:alert

Errors: 404 when no alert with this id exists on your platform.

### List open alerts

 - [GET /alerts](https://docs.transportial.com/apis/alert/get:alerts.md): Returns every open alert of your platform: alerts that are neither solved nor ignored. The list is not paginated. The request is stored as the window's alert search, so open alerts that later appear, are solved or are ignored for the same scope are pushed to the client as live updates.

Permission: get:alerts

### List open alerts of several entities

 - [POST /alerts](https://docs.transportial.com/apis/alert/get:alerts:entities.md): Returns the open alerts (neither solved nor ignored) of your platform whose related entities include any of the entities in the body. The list is not paginated. An empty entities list returns no alerts. The request is stored as the window's alert search, so open alerts that later appear, are solved or are ignored for the same scope are pushed to the client as live updates.

Permission: get:alerts

Errors: 400 when the body is not valid JSON or does not match the request.

### List open alerts of an entity

 - [GET /alerts/{entityType}/{entityId}](https://docs.transportial.com/apis/alert/get:alerts:entity.md): Returns the open alerts (neither solved, ignored, archived nor deleted) of your platform whose related entities include the given entity. The list is not paginated. The request is stored as the window's alert search, so open alerts that later appear, are solved or are ignored for the same scope are pushed to the client as live updates.

Permission: get:alerts

Errors: 404 when entityType is not a known entity type.

### List open alerts of an entity (paging ignored)

 - [GET /alerts/{entityType}/{entityId}/{offset}/{amount}](https://docs.transportial.com/apis/alert/get:alerts:entity:paged.md): Same as GET /alerts/{entityType}/{entityId}: returns the open alerts (neither solved, ignored, archived nor deleted) of your platform whose related entities include the given entity. offset and amount are accepted but not applied: all open alerts are returned. The request is stored as the window's alert search, so open alerts that later appear, are solved or are ignored for the same scope are pushed to the client as live updates.

Permission: get:alerts

Errors: 404 when entityType is not a known entity type.

### Get an alert

 - [GET /alert/{id}](https://docs.transportial.com/apis/alert/get:alert.md): Returns one alert of your platform by id, whether open, solved or ignored. Archived and deleted alerts are not found.

Permission: get:alert

Errors: 404 when no alert with this id exists on your platform.

## Complaint

Complaints registered on a platform about a chauffeur, vehicle, user, location or business, recorded as free text. They can be listed per subject or searched with id filters. Deleting is a soft delete.

### List complaints about a vehicle

 - [GET /complaints/vehicle/{id}/{offset}/{amount}](https://docs.transportial.com/apis/complaint/get:vehicle:complaints.md): Returns one page of the complaints of your platform linked to the given vehicle, most recently updated first. Deleted and archived complaints are not included. An unknown vehicle id returns an empty page.

Permission: filter:ComplaintsByVehicle

Errors: 400 when offset is not an integer.

### List complaints linked to a user

 - [GET /complaints/user/{id}/{offset}/{amount}](https://docs.transportial.com/apis/complaint/get:user:complaints.md): Returns one page of the complaints of your platform linked to the given user, most recently updated first. Deleted and archived complaints are not included. An unknown user id returns an empty page.

Permission: filter:ComplaintsByUser

Errors: 400 when offset is not an integer.

### List complaints about a chauffeur

 - [GET /complaints/chauffeur/{id}/{offset}/{amount}](https://docs.transportial.com/apis/complaint/get:chauffeur:complaints.md): Returns one page of the complaints of your platform linked to the given chauffeur, most recently updated first. Deleted and archived complaints are not included. An unknown chauffeur id returns an empty page.

Permission: filter:ComplaintsByChauffeur

Errors: 400 when offset is not an integer.

### List complaints

 - [GET /complaints/{offset}/{amount}](https://docs.transportial.com/apis/complaint/get:complaints.md): Returns one page of the complaints of your platform, most recently updated first. Deleted and archived complaints are not included.

Permission: get:Complaints

Errors: 400 when offset is not an integer.

### Search complaints

 - [POST /complaints/{offset}/{amount}](https://docs.transportial.com/apis/complaint/search:complaints.md): Searches the complaints of your platform (search index) with optional filters on chauffeur, vehicle, user, location and business. Deleted and archived complaints are never returned. Results are ordered by relevance unless sortBy is given.

Complaints do not build a search string today, so a query containing letters or digits matches nothing; use the id filters instead.

A failing search (for example an unsortable sortBy field) is logged and returns an empty page.

Permission: get:Complaints

Errors: 400 when the body is not a JSON object or offset is not an integer.

### List complaints about a business

 - [GET /complaints/business/{id}/{offset}/{amount}](https://docs.transportial.com/apis/complaint/get:business:complaints.md): Returns one page of the complaints of your platform linked to the given business, most recently updated first. Deleted and archived complaints are not included. An unknown business id returns an empty page.

Permission: filter:ComplaintsByBusiness

Errors: 400 when offset is not an integer.

### Get a complaint

 - [GET /complaint/{id}](https://docs.transportial.com/apis/complaint/get:complaint.md): Returns one complaint of your platform by id. Deleted complaints are not returned.

Permission: get:Complaint

Errors: 404 when no complaint with this id exists on your platform.

### Delete a complaint

 - [DELETE /complaint/{id}](https://docs.transportial.com/apis/complaint/delete:complaint.md): Soft-deletes a complaint: it is marked deleted and no longer returned by lists, filters and searches.

Permission: delete:Complaint

Errors: 404 when no complaint with this id exists on your platform.

### Update a complaint

 - [PUT /complaint](https://docs.transportial.com/apis/complaint/update:complaint.md): Updates the complaint identified by id in the body. The body is merged onto the stored complaint: fields that are left out keep their value. An id that does not exist yet creates a new complaint.

Permission: update:Complaint

Errors: 400 when the body is not valid JSON or contains an invalid value; 404 when the id belongs to a complaint of another platform.

### Create a complaint

 - [POST /complaint](https://docs.transportial.com/apis/complaint/create:complaint.md): Registers a complaint on your platform. The body is read as a complaint model; link the subject(s) by sending objects with the id of an existing chauffeur, vehicle, user, location and/or business.

Permission: create:Complaint

Errors: 400 when the body is not valid JSON or does not match the complaint model.

### List complaints for a location

 - [GET /complaints/location/{id}/{offset}/{amount}](https://docs.transportial.com/apis/complaint/get:location:complaints.md): Returns one page of the complaints of your platform linked to the given location, most recently updated first. Deleted and archived complaints are not included. An unknown location id returns an empty page.

Permission: get:Complaints

Errors: 400 when offset is not an integer.

## Document

Business documents such as CMRs, proofs of delivery, delivery notes and photos, attached to orders, consignments, trips, actions and goods. Content is either a `uri` to a stored file or inline base64 `data`; inline data is uploaded as a file on save and replaced by a `uri`. Documents can be marked verified or unverified. Deleting is a soft delete that also removes the document from the entities it was attached to. Customer-portal users only see documents of their own transport orders.

### Get a document analysis

 - [GET /document-inbox/analysis/{id}](https://docs.transportial.com/apis/document/get:documentanalysis.md): Returns one analysis of the document inbox with everything read from the document, the link candidates and the conflicts with the linked order.

Requires the documentIntelligence feature on your platform; without it every document inbox endpoint answers 400 with document_intelligence_disabled.

Permission: get:documentIntelligence

Errors: 400 with document_intelligence_disabled; 404 when the analysis does not exist on your platform.

### Link a document to an order, consignment or trip

 - [PUT /document-inbox/analysis/{id}/link](https://docs.transportial.com/apis/document/link:documentanalysis.md): Moves the document to the given order, consignment or trip (by id or by number) and compares it with that entity again. Send no entityId and no reference to unlink. A manual link removes the link-related review reasons.

Requires the documentIntelligence feature on your platform; without it every document inbox endpoint answers 400 with document_intelligence_disabled.

Permission: update:documentIntelligence

Errors: 400 with document_intelligence_disabled or for an unknown entityType; 404 when the analysis or the entity does not exist on your platform.

### Mark a document as not verified

 - [GET /document/{id}/unverify](https://docs.transportial.com/apis/document/unverify:document.md): Sets verified back to false on the document. Idempotent. Note that this is a GET request that changes data.

Permission: unverify:document

Errors: 404 when no document with this id exists on your platform.

### Get the required-documents status of orders, consignments or trips

 - [POST /required-documents/status](https://docs.transportial.com/apis/document/lookup:requireddocumentsstatus.md): Evaluates the platform's required-document rules (complianceSettings.requiredDocumentsSettings) for up to 200 transport orders, consignments or trips: per requirement how many documents are needed and received, the traffic light (none, missing, partial, complete) and the strictest invoicing gate of the unmet requirements. A document counts when it is linked to the entity (or its consignments, stops or trip stops) and classified as the required type by the AI model, by our QR code or by hand. Computed on every call; nothing is stored. Customer portal users get an empty list.

Without active rules every requirement list is empty.

Permission: get: (get:transportOrder, get:consignment or get:trip)

Errors: 400 with required_documents_permission, an unknown entityType or more than 200 ids.

### Get the QR codes for paperwork

 - [POST /paperwork/codes](https://docs.transportial.com/apis/document/lookup:paperworkcodes.md): Returns the QR code (payload TRPD1: and short id XXXX-XXXX-XXXX) to print on a CMR, transport order, trip sheet, delivery note or document template of an order, consignment or trip. The code is opaque and random, created on first use and reused on reprints. When a scanned copy carrying it comes back through the document inbox, it is linked to that entity with confidence 1 without the AI model. Kinds switched off in documentIntelligenceSettings.paperworkQr, entities not on your platform and customer portal users get no code.

Requires the documentIntelligence feature on your platform.

Permission: get: per item

Errors: 400 with document_intelligence_disabled, paperwork_code_permission or more than 100 items.

### Check whether required documents allow invoicing

 - [POST /required-documents/invoice-check](https://docs.transportial.com/apis/document/check:invoicerequireddocuments.md): Checks the transport orders an invoice is (or will be) made from against the required-document rules: ok, warn (ask the user to confirm) or block (creating or sending the invoice is refused with required_documents_missing), with the unmet requirements per order. Send transportOrderIds, or invoiceId for an existing invoice.

Always ok while the platform has no active rules.

Permission: get:invoice

Errors: 400 with more than 200 ids; 404 when the invoice is not on your platform.

### List documents

 - [GET /documents/{offset}/{amount}](https://docs.transportial.com/apis/document/get:documents.md): Returns one page of the documents of your platform, most recently updated first. Deleted and archived documents are not included.

Customer-portal users only get the documents of their own transport orders; for them amount is not capped.

Permission: get:documents

Errors: 400 when offset is not an integer.

### Search documents

 - [POST /documents/{offset}/{amount}](https://docs.transportial.com/apis/document/search:documents.md): Full-text search over the documents of your platform (search index), newest first. Deleted and archived documents are never returned.

Documents do not build a search string today, so a non-blank query matches no documents for regular users; send {} to page through all documents. Customer-portal users search only the documents of their own transport orders, where query matches name, file name and description by substring.

A failing search is logged and returns an empty page.

Permission: search:documents

Errors: 400 when the body is not a JSON object or offset is not an integer.

### Mark a document as verified

 - [GET /document/{id}/verify](https://docs.transportial.com/apis/document/verify:document.md): Sets verified to true on the document, for example after a planner has checked a returned CMR. Idempotent. Note that this is a GET request that changes data.

Permission: verify:document

Errors: 404 when no document with this id exists on your platform.

### Get a document

 - [GET /document/{id}](https://docs.transportial.com/apis/document/get:document.md): Returns one document of your platform by id, including its content, file and identifiers. Deleted documents are not returned. Customer-portal users can only read documents that belong to their own transport orders.

Permission: get:document

Errors: 404 when no document with this id exists on your platform or a portal user may not see it.

### Delete a document

 - [DELETE /document/{id}](https://docs.transportial.com/apis/document/delete:document.md): Soft-deletes a document: it is marked deleted, disappears from lists and searches and from the orders, consignments, trips, actions and goods it was attached to. The stored file is kept.

Permission: delete:document

Errors: 404 when no document with this id exists on your platform.

### Process a document again

 - [POST /document-inbox/analysis/{id}/reprocess](https://docs.transportial.com/apis/document/reprocess:documentanalysis.md): Queues the document for processing again, for example after a failure, a skipped document (monthly limit, processing off) or a change of the document types. An existing link is kept.

Requires the documentIntelligence feature on your platform; without it every document inbox endpoint answers 400 with document_intelligence_disabled.

Permission: upload:documentIntelligence

Errors: 400 with document_intelligence_disabled; 404 when the analysis does not exist on your platform.

### Search the document inbox

 - [POST /document-inbox/{offset}/{amount}](https://docs.transportial.com/apis/document/search:documentinbox.md): Lists the analysed documents of your platform, newest first, with the platform's document types. Filters on text, status, document type, open conflicts, missing link and signature. Documents that have been queued or processing for longer than staleAfterMinutes are queued again by this call.

Requires the documentIntelligence feature on your platform; without it every document inbox endpoint answers 400 with document_intelligence_disabled.

Permission: get:documentIntelligence

Errors: 400 with document_intelligence_disabled, or when the body is not valid JSON.

### Put files into the document inbox

 - [POST /document-inbox/upload](https://docs.transportial.com/apis/document/upload:documentinbox.md): Creates a document for every uploaded file (at most 500 per call) and queues it for document intelligence: classification, reading references, dates, addresses, goods and signatures, linking to an order, consignment or trip, and comparing with it. Processing runs in the background; poll the inbox or the analysis for the result. Files of other platforms are ignored.

Requires the documentIntelligence feature on your platform; without it every document inbox endpoint answers 400 with document_intelligence_disabled.

Permission: upload:documentIntelligence

Errors: 400 with document_intelligence_disabled, when fileIds is empty or has more than 500 entries, or the body is invalid.

### Update a document

 - [PUT /document](https://docs.transportial.com/apis/document/update:document.md): Updates the document identified by id in the body. The body is merged onto the stored document: fields that are left out keep their value. An id that does not exist yet creates a new document. Inline data content is uploaded and converted to uri content as on create.

Permission: update:document

Errors: 400 when the body is not valid JSON or contains an invalid value; 404 when the id belongs to a document of another platform.

### Create a document

 - [POST /document](https://docs.transportial.com/apis/document/create:document.md): Creates a document on your platform from the document model in the body.

- When content is inline data content, the base64 bytes are uploaded as a file (purpose documents; stored as an image when mimeType starts with image/, otherwise as a PDF), file is set to the new file and content becomes uri content pointing at it.
- A file reference is resolved on your platform by id or url; an unknown file is stored as a new file record.

Permission: create:document

Errors: 400 when the body is not valid JSON, does not match the document model, or inline data is not valid base64.

### Copy a document value to the order

 - [POST /document-inbox/analysis/{id}/conflict/{field}/apply](https://docs.transportial.com/apis/document/apply:documentconflict.md): Copies the document's value of an open, copyable conflict onto the order or consignment through the normal save (so its hooks and integrations run) and marks the conflict applied. Also needs update:transportOrder or update:consignment for the entity the value goes to.

Requires the documentIntelligence feature on your platform; without it every document inbox endpoint answers 400 with document_intelligence_disabled.

Permission: update:documentIntelligence

Errors: 400 with document_intelligence_disabled, document_conflict_not_copyable or document_conflict_permission; 404 when the analysis or an open conflict for the field does not exist.

### Dismiss a document conflict

 - [POST /document-inbox/analysis/{id}/conflict/{field}/dismiss](https://docs.transportial.com/apis/document/dismiss:documentconflict.md): Marks an open conflict dismissed; it stays dismissed when the document is processed again while both values are unchanged.

Requires the documentIntelligence feature on your platform; without it every document inbox endpoint answers 400 with document_intelligence_disabled.

Permission: update:documentIntelligence

Errors: 400 with document_intelligence_disabled; 404 when the analysis or an open conflict for the field does not exist.

### Get the analyses of documents

 - [POST /document-inbox/documents](https://docs.transportial.com/apis/document/lookup:documentanalyses.md): Returns the analyses of the given documents (at most 200), for the type, signature and conflict badges on an order, consignment or trip documents tab. With analyzeAttachedDocuments on, documents of that entity that were never analysed are queued now, linked to the entity.

Requires the documentIntelligence feature on your platform; without it every document inbox endpoint answers 400 with document_intelligence_disabled.

Permission: get:documentIntelligence

Errors: 400 with document_intelligence_disabled, or when the body is not valid JSON.

### Set the type of a document by hand

 - [PUT /document/{id}/classification](https://docs.transportial.com/apis/document/classify:document.md): Sets the document type (and whether it is signed) of a document by hand. The type counts for required documents exactly like a classification by the AI model, so required documents also work without document intelligence; a later AI reprocess keeps it. Send an empty documentType to clear it. Customer portal users are refused.

Requires active required-document rules or the documentIntelligence feature on your platform.

Permission: update:document

Errors: 400 with required_documents_disabled or an unknown document type; 404 when the document is not on your platform.

## ConsignmentNote

A consignment note is an electronic CMR (eCMR) under the e-CMR Additional Protocol to the CMR convention, one per consignment. Issuing freezes the particulars (boxes 1-24) as the first entry of an append-only log in which every entry is SHA-256 hashed and chained to the previous one; signatures (sender, carrier, consignee, successive carriers, each with name, drawn image, device time and position, bound to the current head hash), reservations, amendments (corrections and the sender's right of disposal), delivery and cancellation are appended, never edited. The note moves issued -> in_transit (required take-over signatures present) -> delivered, or is cancelled before anyone signs. Sender and consignee can sign through their own share links, authorities read the note through an inspection link (also a QR code on the PDF), drivers work through their trip, and a PDF rendition sealed with the organisation's certificate is generated per state. When its CMR is neutral, the note of a consignment executed by a subcontractor shows neutral parties in boxes 1 and 2 (see `POST /neutralCmr:resolve`).

### Set the contacts of the share links

 - [PUT /consignmentNote/{id}/contacts](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:contacts.md): Sets the e-mail address and phone number that receive the one-time codes of the sender and consignee links. The contacts are not part of the legal particulars and are erased when the note is anonymised.

Permission: update:consignment

Errors: 400 the note was anonymised (consignment_note_already_anonymised); 404 no eCMR with this id exists on your platform.

### Confirm delivery without consignee signature from the trip

 - [POST /trip/{id}/consignmentNote/{noteId}/deliver](https://docs.transportial.com/apis/consignmentnote/update:trip:consignmentnote:deliver.md): Same as confirming delivery without the consignee's signature, for the driver app. A shareCode query parameter authorises instead of a session.

Permission: update:trip:action, or the trip's share code in the shareCode query parameter

Errors: 400 a rule refuses the delivery; the response message holds the error key; 404 the trip or the eCMR does not exist, or the eCMR's consignment is not on the trip; 409 the note changed since observedHeadHash was read (consignment_note_stale).

### Add a reservation to an eCMR

 - [POST /consignmentNote/{id}/reservation](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:reservation.md): Records a carrier reservation (CMR art. 8, while ISSUED or IN_TRANSIT) or a consignee reservation (art. 30, also after delivery), with optional photos stored as documents.

Permission: update:consignment

Errors: 400 a rule refuses the reservation; the response message holds the error key; 404 no eCMR with this id exists on your platform.

### Confirm delivery without the consignee's signature

 - [POST /consignmentNote/{id}/deliver](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:deliver.md): For a consignee who refuses or is absent: the carrier signs instead and records the reason; the note becomes DELIVERED. Only when the platform allows delivery without consignee signature.

Permission: update:consignment

Errors: 400 a rule refuses the delivery; the response message holds the error key; 404 no eCMR with this id exists on your platform; 409 the note changed since observedHeadHash was read (consignment_note_stale).

### Get an eCMR

 - [GET /consignmentNote/{id}](https://docs.transportial.com/apis/consignmentnote/get:consignmentnote.md): Returns the eCMR with its current particulars (issued particulars with every amendment applied), signatures, reservations, amendments, the hash-chained event log, the result of recomputing the chain and the share links.

Permission: read:consignment

Errors: 404 no eCMR with this id exists on your platform.

### Add a reservation from the trip

 - [POST /trip/{id}/consignmentNote/{noteId}/reservation](https://docs.transportial.com/apis/consignmentnote/update:trip:consignmentnote:reservation.md): Same as adding a reservation, for the driver app. A shareCode query parameter authorises instead of a session.

Permission: update:trip:action, or the trip's share code in the shareCode query parameter

Errors: 400 a rule refuses the reservation; the response message holds the error key; 404 the trip or the eCMR does not exist, or the eCMR's consignment is not on the trip.

### Cancel an eCMR

 - [POST /consignmentNote/{id}/cancel](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:cancel.md): Cancels an eCMR that nobody has signed yet; a new one can then be issued for the consignment.

Permission: update:consignment

Errors: 400 the note is not ISSUED or already signed, or no reason was given; 404 no eCMR with this id exists on your platform.

### Anonymise an eCMR after its retention period

 - [POST /consignmentNote/{id}/anonymise](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:anonymise.md): Erases the personal data of the note once its retention period (platform eCMR setting retentionYears, counted from the last event) has passed: actor names, user ids and positions of the events, IP addresses and user agents, signer names, signature images and the link contacts. The legal particulars stay. The hash chain keeps verifying because events hash personal data only through a salted commitment; events recorded in the legacy format are left untouched. Recorded as an ANONYMISED event.

Permission: update:consignment

Errors: 400 the retention period has not passed (consignment_note_retention_active) or the note was already anonymised (consignment_note_already_anonymised); 404 no eCMR with this id exists on your platform.

### Amend the particulars of an eCMR

 - [POST /consignmentNote/{id}/amend](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:amend.md): Records a correction or the sender's right of disposal (CMR art. 12) as an AMENDMENT event that replaces the given top-level particulars. Nothing already recorded changes. Only while ISSUED or IN_TRANSIT.

Permission: update:consignment

Errors: 400 a rule refuses the amendment; the response message holds the error key; 404 no eCMR with this id exists on your platform; 409 the note changed since observedHeadHash was read (consignment_note_stale).

### Add a consignee reservation through a share link

 - [POST /consignmentNote:shared/{id}/{shareCode}/reservation](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:shared:reservation.md): Records a consignee reservation (CMR art. 30) through the consignee link; party is forced to CONSIGNEE.

Permission: Public, no authentication (the share code authorises the request).

Errors: 400 a rule refuses the reservation; the response message holds the error key; 401 unknown note, wrong or expired code, or not a consignee link.

### Issue an eCMR for a consignment

 - [POST /consignment/{id}/consignmentNote](https://docs.transportial.com/apis/consignmentnote/create:consignmentnote.md): Freezes the given particulars (or the built ones when omitted) as the ISSUED event of a new eCMR, assigns the next number (-) and creates the sender, consignee and inspection share links. Requires the platform feature ecmr (without it the platform only has the paper CMR PDF); a consignment holds at most one non-cancelled eCMR.

Permission: update:consignment

Errors: 400 the platform does not have the ecmr feature (consignment_note_disabled) or the consignment already has an eCMR (consignment_note_already_issued); 404 no consignment with this id exists on your platform.

### Anonymise every eCMR whose retention period has passed

 - [POST /consignmentNotes:anonymiseExpired](https://docs.transportial.com/apis/consignmentnote/update:consignmentnotes:anonymiseexpired.md): Anonymises up to 200 notes of your platform whose retention period has passed, oldest first, exactly like anonymising a single note. Call again until it returns 0.

Permission: update:consignment

Errors: 401 when not authenticated or missing the permission.

### Download the sealed eCMR PDF

 - [GET /consignmentNote/{id}/pdf](https://docs.transportial.com/apis/consignmentnote/get:consignmentnote:pdf.md): Returns the PDF rendition of the eCMR's current state, sealed with the organisation's certificate when one is configured. Generated on first request per head hash and stored with the note.

Permission: read:consignment

Errors: 404 no eCMR with this id exists on your platform.

### Preview the eCMR particulars of a consignment

 - [GET /consignment/{id}/consignmentNote:preview](https://docs.transportial.com/apis/consignmentnote/get:consignmentnote:preview.md): Builds the CMR particulars (boxes 1-24) from the consignment, its transport order and trips, with the same fallbacks as the paper CMR (sender = customer, consignee = last stop). Nothing is saved; send the reviewed particulars to the issue endpoint.

Permission: read:consignment

Errors: 404 no consignment with this id exists on your platform.

### Sign an eCMR from the trip

 - [POST /trip/{id}/consignmentNote/{noteId}/sign](https://docs.transportial.com/apis/consignmentnote/update:trip:consignmentnote:sign.md): Same as signing an eCMR, for the driver app: the sender, carrier or consignee signs on the driver's device. A shareCode query parameter authorises instead of a session.

Permission: update:trip:action, or the trip's share code in the shareCode query parameter

Errors: 400 a rule refuses the signature; the response message holds the error key; 404 the trip or the eCMR does not exist, or the eCMR's consignment is not on the trip; 409 the note changed since observedHeadHash was read (consignment_note_stale).

### Issue missing eCMRs for a trip

 - [POST /trip/{id}/consignmentNotes:issue](https://docs.transportial.com/apis/consignmentnote/create:trip:consignmentnotes.md): Issues an eCMR for every consignment on the trip that has none, with particulars built from the TMS data. Only when the platform allows drivers to issue eCMRs. A shareCode query parameter authorises instead of a session.

Permission: update:trip:action, or the trip's share code in the shareCode query parameter

Errors: 400 the platform does not have the ecmr feature, or driver issuing is disabled; 404 no trip with this id exists on your platform.

### Resolve the neutral CMR parties

 - [POST /neutralCmr:resolve](https://docs.transportial.com/apis/consignmentnote/resolve:neutralcmr.md): Returns, per consignment, the neutral parties that replace the sender (box 1) and consignee (box 2) on the paperwork the executing carrier gets, so a client can print the neutral CMR next to the real one. Settings come from the consignment's override, else the forwarded trip's override, else the trip's capacity contract (neutralCmr). Only consignments executed by a carrier (a forwarded trip, or the carrier's copy of a forwarded order) whose CMR is neutral are returned; the places of taking over and delivery always stay real. Read-only.

Permission: get:trip, get:transportOrder or get:consignment

Errors: 400 when the body is not valid JSON; 404 when the trip or transport order is not found on your platform.

### Download the sealed eCMR PDF from the trip

 - [GET /trip/{id}/consignmentNote/{noteId}/pdf](https://docs.transportial.com/apis/consignmentnote/get:trip:consignmentnote:pdf.md): Same as downloading the sealed eCMR PDF, for the driver app. A shareCode query parameter authorises instead of a session.

Permission: read:trip, or the trip's share code in the shareCode query parameter

Errors: 404 the trip or the eCMR does not exist, or the eCMR's consignment is not on the trip.

### List the eCMRs of a trip

 - [GET /trip/{id}/consignmentNotes](https://docs.transportial.com/apis/consignmentnote/get:trip:consignmentnotes.md): The non-cancelled eCMRs of the consignments on the trip, for the driver app. A shareCode query parameter (the trip's share code) authorises instead of a session.

Permission: read:trip, or the trip's share code in the shareCode query parameter

Errors: 404 no trip with this id exists on your platform.

### Sign an eCMR through a share link

 - [POST /consignmentNote:shared/{id}/{shareCode}/sign](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:shared:sign.md): Signs as the link's party: SENDER for the sender link, CONSIGNEE for the consignee link (the role in the body is ignored). Inspection links cannot sign.

Permission: Public, no authentication (the share code authorises the request).

Errors: 400 a rule refuses the signature; the response message holds the error key; 401 unknown note, wrong or expired code, or an inspection link; 409 the note changed since observedHeadHash was read (consignment_note_stale).

### Request a one-time code for a share link

 - [POST /consignmentNote:shared/{id}/{shareCode}/verification](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:shared:verification.md): Sends a 6-digit code to the contact stored for the link's party (sender or consignee) through the message automation chosen in the platform's eCMR settings. The code is needed to sign or add a reservation through the link when the platform requires link verification. Codes expire after the configured minutes, allow five attempts and can be re-requested after 60 seconds; only their hash is stored.

Permission: Public, no authentication (the share code authorises the request).

Errors: 400 no contact or no message automation is configured (consignment_note_verification_unavailable), or a code was sent less than a minute ago (consignment_note_verification_throttled); 401 unknown note, wrong or expired code.

### Open an eCMR through a share link

 - [GET /consignmentNote:shared/{id}/{shareCode}](https://docs.transportial.com/apis/consignmentnote/get:consignmentnote:shared.md): Returns the eCMR to whoever holds a sender, consignee or inspection link, without share links but with access, canSign, canAddReservation and the terms. Inspection links are read-only and meant for authorities.

Permission: Public, no authentication (the share code authorises the request).

Errors: 401 unknown note, wrong or expired code.

### Sign an eCMR

 - [POST /consignmentNote/{id}/sign](https://docs.transportial.com/apis/consignmentnote/update:consignmentnote:sign.md): Records a signature (name, drawn image, device time and position) bound to the note's current head hash. Sender and carrier sign once while the note is ISSUED or IN_TRANSIT; once every required take-over signature is present the note becomes IN_TRANSIT. The consignee signs while IN_TRANSIT and the note becomes DELIVERED. Reservations sent along are recorded just before the signature.

Permission: update:consignment

Errors: 400 a rule refuses the signature; the response message holds the error key; 404 no eCMR with this id exists on your platform; 409 the note changed since observedHeadHash was read (consignment_note_stale).

### Export an eCMR as JSON

 - [GET /consignmentNote/{id}/export](https://docs.transportial.com/apis/consignmentnote/get:consignmentnote:export.md): Returns the complete record as a JSON file for the data owner's own archive: particulars, signatures, reservations, amendments, every event with its hash inputs, the exact canonical payloads and a recipe to re-verify the hash chain independently. Share links and link contacts are left out.

Permission: read:consignment

Errors: 404 no eCMR with this id exists on your platform.

### Download the sealed eCMR PDF through a share link

 - [GET /consignmentNote:shared/{id}/{shareCode}/pdf](https://docs.transportial.com/apis/consignmentnote/get:consignmentnote:shared:pdf.md): Returns the sealed PDF of the eCMR's current state to whoever holds a sender, consignee or inspection link.

Permission: Public, no authentication (the share code authorises the request).

Errors: 401 unknown note, wrong or expired code.

### List the eCMRs of a consignment

 - [GET /consignment/{id}/consignmentNotes](https://docs.transportial.com/apis/consignmentnote/get:consignmentnotes:consignment.md): All eCMRs of the consignment, newest first, including cancelled ones, each with its event log, integrity check and share links.

Permission: read:consignment

Errors: 404 no consignment with this id exists on your platform.

## DocumentTemplate

Visual document designs (page settings plus positioned, field-bound elements) that the PDF service renders for an entity such as a transport order, consignment, trip, invoice, quote or business: CMRs, waybills, invoices and custom documents. Templates belong to a platform; a global catalogue maintained by super administrators offers branded starting designs. Designs can be duplicated, generated or modified with AI, and the bindable fields per entity type can be listed. Message automations can attach rendered templates.

### Duplicate a document template

 - [POST /documentTemplate/{id}/duplicate](https://docs.transportial.com/apis/documenttemplate/duplicate:documenttemplate.md): Saves a copy of a document template of your platform, named  (Copy). The copy takes over the document type, entity type, page settings and elements; description and catalogue category are not copied, and createdBy is set to the authenticated user.

Permission: create:documentTemplate

Errors: 404 when no non-archived document template with this id exists on your platform.

### List document templates

 - [GET /documentTemplates/{offset}/{amount}](https://docs.transportial.com/apis/documenttemplate/get:documenttemplates.md): Returns one page of the document templates of your platform (catalogue templates excluded, non-archived, non-deleted), most recently updated first, plus the total number in totalResults.

Permission: get:documentTemplate

Errors: 400 when offset is not an integer.

### Search document templates

 - [POST /documentTemplates/{offset}/{amount}](https://docs.transportial.com/apis/documenttemplate/search:documenttemplates.md): Full-text search over the document templates of your platform, newest first. Returns one page of matches plus the total number of matches in totalResults.

The body is an array of { "key", "value" } filters: name or query (phrase match on name, description, document type, entity type and category), documentType and entityType. Unknown keys or values are ignored, and a body that is not a JSON array is treated as no filters. A failing search returns an empty page instead of an error.

Permission: get:documentTemplate

Errors: 400 when offset is not an integer.

### Load a catalogue template

 - [POST /documentTemplate-template/{id}/duplicate](https://docs.transportial.com/apis/documenttemplate/duplicate:documenttemplate:catalog.md): Returns a copy of a catalogue template ready to open in the designer. The Transportial branding placeholders in its page and elements (logo, colours, portal host) are replaced with your platform's branding, createdBy is set to the authenticated user and template is false. The copy has no id and is not saved; save it with POST /documentTemplate.

Permission: create:documentTemplate

Errors: 404 when the id does not belong to a non-deleted, non-archived catalogue template.

### Update a document template

 - [PUT /documentTemplate](https://docs.transportial.com/apis/documenttemplate/update:documenttemplate.md): Updates the document template identified by the id in the body; fields left out keep their value. Catalogue templates (an id of a catalogue template, or template: true) can only be updated by super administrators and always stay without platform. A body id that is unknown on every platform creates a new template.

Permission: update:documentTemplate (catalogue templates additionally require a super administrator)

Errors: 400 when the body is not valid JSON or does not match the schema; 401 when a non super administrator updates a catalogue template; 404 when the id belongs to a template of another platform.

### Create a document template

 - [POST /documentTemplate](https://docs.transportial.com/apis/documenttemplate/create:documenttemplate.md): Creates a document template on your platform. The server sets createdBy to the authenticated user.

With template: true a catalogue template is created instead: it is stored without platform and offered to every platform through GET /documentTemplate-templates. Only super administrators can create catalogue templates.

Permission: create:documentTemplate (catalogue templates additionally require a super administrator)

Errors: 400 when the body is not valid JSON or does not match the schema; 401 when a non super administrator sends template: true.

### List the template catalogue

 - [GET /documentTemplate-templates](https://docs.transportial.com/apis/documenttemplate/get:documenttemplate:catalog.md): Returns every non-deleted, non-archived catalogue template (global designs every platform can start from), ordered by category and name. The list is not paginated. Use POST /documentTemplate-template/{id}/duplicate to load one into your platform.

Permission: get:documentTemplate

### Get a document template

 - [GET /documentTemplate/{id}](https://docs.transportial.com/apis/documenttemplate/get:documenttemplate.md): Returns a single document template of your platform, including its page settings and elements. Catalogue templates are not returned here; use GET /documentTemplate-templates.

Permission: get:documentTemplate

Errors: 404 when no non-archived document template with this id exists on your platform.

### Delete a document template

 - [DELETE /documentTemplate/{id}](https://docs.transportial.com/apis/documenttemplate/delete:documenttemplate.md): Deletes a document template of your platform. By default the template is soft-deleted (marked deleted and hidden everywhere); with permanent=true it is removed from the database. Catalogue templates can only be deleted by super administrators.

Permission: delete:documentTemplate (catalogue templates additionally require a super administrator)

Errors: 401 when a non super administrator deletes a catalogue template; 404 when no non-archived template with this id exists on your platform and it is not a catalogue template.

### List bindable fields of an entity type

 - [GET /documentTemplate/fields/{entityType}](https://docs.transportial.com/apis/documenttemplate/get:documenttemplate:fields.md): Returns the fields a document template can bind to for an entity type, with their path, label and value type. The entity type is matched case-insensitively and underscores are ignored (transportOrder, TRANSPORT_ORDER); an unknown entity type returns an empty list.

Permission: get:documentTemplate

### Generate a document template with AI

 - [POST /documentTemplate/generate](https://docs.transportial.com/apis/documenttemplate/generate:documenttemplate.md): Proposes a document template design from a free-text description and/or uploaded documents or images, using the AI model configured for your platform. Send currentTemplate to have the AI modify an existing design. The proposal is sanitised for the designer, branded with your platform's logo and colours, and returned with warnings for bindings that do not resolve. It is not saved; save it with POST /documentTemplate.

Permission: create:documentTemplate

Errors: 400 when the body does not match the request schema.

## File

Stored files (images, PDFs, Office documents, CSV, JSON and e-mails) kept in the object store and referenced by documents and other entities. Files are uploaded as multipart/form-data with a purpose. They are downloaded through signed, time-limited URLs (valid for 1 hour) that work without an Authorization header, either as a redirect to the object store or streamed through the API. PDF, Word, Excel and e-mail files can also be converted to HTML or a text table without storing them.

### Download a file

 - [GET /file/{id}/download](https://docs.transportial.com/apis/file/download:file.md): Redirects to a temporary, pre-signed object-store URL of the file (valid for 10 minutes). Browsers and HTTP clients that follow redirects receive the file bytes directly from the object store.

Public, no authentication: browsers load files directly as image, iframe or link sources. Access is controlled by the signed token and expires query parameters that every API response appends to a file url (valid for 1 hour). When the server setting FILE_DOWNLOAD_REQUIRE_TOKEN is on, requests without a valid token are rejected with 404; otherwise tokenless downloads are still served (and logged).

Permission: Public, no authentication.

Errors: 404 when no file with this id exists, or the token is missing or invalid while tokens are required.

### Extract a file as a table

 - [POST /file/toMatrix](https://docs.transportial.com/apis/file/tomatrix:file.md): Extracts the content of an uploaded PDF or Excel file (multipart part file) as a matrix of text cells, without storing the file. The format is derived from the part's Content-Type.

Failures are returned with HTTP 200 and success: false: File is required or File type not supported.

Permission: toMatrix:file

### Download a file through the API

 - [GET /file/{id}/download:proxy](https://docs.transportial.com/apis/file/download:proxy:file.md): Streams the file bytes through the API instead of redirecting to the object store, for clients that cannot follow redirects to another host. The response has Content-Type set to the file's mimeType and Content-Disposition: attachment with the original file name.

Public, no authentication: browsers load files directly as image, iframe or link sources. Access is controlled by the signed token and expires query parameters that every API response appends to a file url (valid for 1 hour). When the server setting FILE_DOWNLOAD_REQUIRE_TOKEN is on, requests without a valid token are rejected with 404; otherwise tokenless downloads are still served (and logged).

Permission: Public, no authentication.

Errors: 404 when no file with this id exists, or the token is missing or invalid while tokens are required.

### Convert a file to HTML

 - [POST /file/toHtml](https://docs.transportial.com/apis/file/tohtml:file.md): Converts an uploaded file (multipart part file) to HTML without storing it. Supported, by the part's Content-Type: PDF, Word (converted as docx), Excel and e-mail (eml, returned as raw text).

Failures are returned with HTTP 200 and success: false: File is required or File type not supported.

Permission: toMatrix:file

### Upload a file

 - [POST /file/upload](https://docs.transportial.com/apis/file/upload:file.md): Uploads one file as multipart/form-data (part file) and stores it in the object store under /images|documents|files/. Returns the new file with its signed download url; reference it from a document or other entity by its id.

- purpose (form field or query parameter, default files) must be one of identification, document, task, files, platform.
- The stored type follows the part's Content-Type: images, PDF, Word, Excel/spreadsheet, CSV, JSON or XML (stored as JSON), and e-mail (msg, eml, rfc822 and application/octet-stream). Other content types are refused.
- The request may be at most 56,000,000 bytes (about 53 MB); larger requests are rejected by the server.
- The file is assigned to the platform of the caller, or on the driver link to the platform that owns the shared trip.

Refusals are returned with HTTP 200 and success: false: File is required, File type not supported, or File purpose not supported.

Permission: Authenticated (no specific permission). Without a session, the public driver link may upload by passing the trip's shareCode (and preferably tripId); the code must open a trip whose share link is active.

Errors: 403 when there is no session and no valid shareCode.

### Get a file

 - [GET /file/{id}](https://docs.transportial.com/apis/file/get:file.md): Returns the metadata of a stored file by id, including its signed download url (valid for 1 hour) and identifiers. The file bytes are downloaded through the url.

Permission: get:file

Errors: 404 when no file with this id exists on the caller's platform, including files of another platform and deleted or archived files.

## Board

A board is a kanban board on your platform: an ordered set of lanes, each holding todos. A new board sent without lanes gets three default lanes (To do, In progress, Done) mapped to the matching todo statuses. Boards return per-lane todo counts and comment/document totals in statistics. Deleting a board soft-deletes it.

### Create a board

 - [POST /board](https://docs.transportial.com/apis/board/create:board.md): Creates a board owned by the caller (creator).

When lanes is empty or left out, three default lanes are created: To do (status todo), In progress (in_progress) and Done (completed). Lanes and todos nested in the body are created with positions following their order in the request.

Permission: create:board

### List boards

 - [GET /boards/{offset}/{amount}](https://docs.transportial.com/apis/board/get:boards.md): Returns one page of the boards of your platform, most recently updated first. Archived and deleted boards are excluded. Each board carries per-lane todo counts and comment/document totals in statistics.

Permission: get:boards

### Search boards

 - [POST /boards/{offset}/{amount}](https://docs.transportial.com/apis/board/search:boards.md): Full-text search over the non-deleted, non-archived boards of your platform, newest first.

query (non-alphanumeric characters are treated as spaces) is matched against the board's search text: title plus the search text of its lanes and their todos. An empty body or blank query returns every board.

Permission: search:boards

### Get a board

 - [GET /board/{id}](https://docs.transportial.com/apis/board/get:board.md): Returns a single board with its lanes (and their todos), users, creator and statistics.

Permission: get:board

Errors: 404 when no board with this id exists on your platform.

### Update a board

 - [PUT /board/{id}](https://docs.transportial.com/apis/board/update:board.md): Merges the request body into the stored board. Fields left out keep their stored value. Lanes and todos sent in the body are attached to this board and stamped with its platform.

Permission: update:board

Errors: 400 when the path id and the body id differ; 404 when the board belongs to another platform.

### Delete a board

 - [DELETE /board/{id}](https://docs.transportial.com/apis/board/delete:board.md): Soft-deletes a board: it is flagged deleted and no longer returned by the list and search endpoints. Its lanes and todos are left untouched.

Permission: delete:board

Errors: 404 when no board with this id exists on your platform.

## Lane

A lane is an ordered column on a board that holds todos by position. A lane can carry a todo status (for example in_progress) that it gives to todos created in it or moved into it through the todo update endpoint, so moving a card between lanes changes its status. Lanes and todos are renumbered from 0 whenever an item is inserted or moved. Deleting a lane removes it permanently, together with its todos.

### Get a lane

 - [GET /board/lane/{id}](https://docs.transportial.com/apis/lane/get:lane.md): Returns a single lane with its todos in lane order and its board.

Permission: get:lane

Errors: 404 when no lane with this id exists on your platform.

### Update a lane

 - [PUT /board/lane/{id}](https://docs.transportial.com/apis/lane/update:lane.md): Merges the request body into the stored lane. Fields left out keep their stored value. When position differs from the number of lanes on the board, the lane is moved to that position and the board's lanes are renumbered.

Permission: update:lane

Errors: 400 when the path id and the body id differ; 404 when the lane belongs to another platform.

### Delete a lane

 - [DELETE /board/lane/{id}](https://docs.transportial.com/apis/lane/delete:lane.md): Removes the lane from its board and permanently deletes it. The lane's todos are deleted with it (cascade).

Permission: delete:lane

Errors: 404 when no lane with this id exists on your platform.

### Add a lane to a board

 - [POST /board/{id}/lane](https://docs.transportial.com/apis/lane/create:lane.md): Creates a lane on the board and returns the board.

The lane is inserted at position (-1 or a position past the end appends it) and all lanes of the board are renumbered from 0. Todos nested in toDos are created in the lane, positioned in the order given.

Permission: create:lane

Errors: 404 when the board does not exist on your platform.

### List lanes

 - [GET /board/lanes/{offset}/{amount}](https://docs.transportial.com/apis/lane/get:lanes.md): Returns one page of the lanes of all boards on your platform, most recently updated first, each with its todos.

Permission: get:lanes

### Search lanes

 - [POST /board/lanes/{offset}/{amount}](https://docs.transportial.com/apis/lane/search:lanes.md): Full-text search over the lanes of your platform, most recently updated first.

query is matched as a phrase (slop 10) against the lane's search text: title, position, status and the search text of its todos. An empty body or blank query returns every lane.

Permission: search:lanes

### Move a lane within its board

 - [PUT /board/lane/{id}/change-position/{position}](https://docs.transportial.com/apis/lane/update:lane:position.md): Moves the lane to position on its board and renumbers all lanes from 0. A position of -1 or past the last lane moves it to the end.

Permission: update:lane

Errors: 404 when the lane does not exist on your platform or is not on a board.

### List lanes of a board

 - [GET /board/{id}/lanes/{offset}/{amount}](https://docs.transportial.com/apis/lane/get:board:lanes.md): Intended to list the lanes of one board. The board id is currently not applied as a filter: the endpoint returns the same page as GET /board/lanes/{offset}/{amount}, the lanes of every board on your platform.

Permission: get:lanes

## ToDo

A todo is a task card in a board lane, with a title, a Draft.js rich-text description, a deadline, a reporter and an assignee. Its statuses (todo, in_progress, completed) follow the lane it sits in; the server adds overDue when a todo is loaded after its deadline and is not completed. A todo can be linked to any other entity (trip, order, vehicle) through relatedEntityType/relatedEntityId, carries document attachments and comments, and notifies its assignee and any users @mentioned in it. Creating, updating and assigning a todo fire integration triggers.

### List a user's todos

 - [GET /todos/user/{user}/{offset}/{amount}](https://docs.transportial.com/apis/todo/get:todos:user.md): Returns one page of the todos of your platform that the user is assigned to or reported.

totalResults is the number of todos on this page, not the total number of matches.

Permission: get:toDos

Errors: 404 when the user does not exist on your platform.

### Get a todo

 - [GET /board/lane/todo/{id}](https://docs.transportial.com/apis/todo/get:todo.md): Returns a single todo of your platform with its documents, reporter, assignee and comment/attachment counts.

Loading a todo whose deadline has passed adds the overDue status (unless it is already completed) and persists that change.

Permission: get:toDo

Errors: 404 when no todo with this id exists on your platform.

### Update a todo

 - [PUT /board/lane/todo/{id}](https://docs.transportial.com/apis/todo/update:todo.md): Merges the request body into the stored todo. Fields left out keep their stored value.

Behaviour:
- When the new deadline lies in the future, the overDue status is removed.
- When lane references the todo's current lane, the todo moves to position within it and the other todos are renumbered.
- When lane references a different lane, the todo's statuses are replaced by the target lane's todoStatus (overDue is kept), it is removed from the old lane and inserted at position in the new one.
- When the assignee changes to someone other than the caller, that user receives an in-app notification. Users newly @mentioned in the description are notified too.
- The AfterUpdatingTodo integration trigger fires, plus AfterAssigningTodo when the assignee changed.

Permission: update:toDo

Errors: 400 when the path id and the body id differ; 404 when the todo belongs to another platform or the referenced lane does not exist on your platform.

### Delete a todo

 - [DELETE /board/lane/todo/{id}](https://docs.transportial.com/apis/todo/delete:todo.md): Soft-deletes a todo: it is flagged deleted and no longer returned by the list endpoints. Its lane position is not renumbered.

Permission: delete:toDo

Errors: 404 when no todo with this id exists on your platform.

### Get todo statistics of a user

 - [GET /todo:statistics/user/{id}](https://docs.transportial.com/apis/todo/get:todo:statistics:user.md): Counts the non-deleted todos on boards of your platform that are assigned to the user or sit on a board the user created, in total and per status (todo, overDue, in_progress, completed). Todos that are not in a lane on a board are not counted.

Permission: any authenticated user for their own user id; get:toDo:statistics for another user.

Errors: 401 for another user without get:toDo:statistics; 404 when the user does not exist on your platform.

### Move a todo within or between lanes

 - [PUT /board/lane/{lane_id}/todo/{id}/change-position/{position}](https://docs.transportial.com/apis/todo/update:todo:position.md): Moves a todo to position in the lane lane_id and returns that lane.

- Same lane: the todo is moved and the lane's todos are renumbered from 0.
- Other lane: the todo is removed from its current lane (which is renumbered) and inserted at position in the target lane. Its statuses are not changed; use PUT /board/lane/todo/{id} to move it with the target lane's status.

A position of -1 or past the last todo moves it to the end.

Permission: update:toDo

Errors: 404 when the todo does not exist on your platform or is not in a lane, or when the target lane does not exist on your platform.

### Get a todo comment

 - [GET /toDo/comment/{id}](https://docs.transportial.com/apis/todo/get:todocomment.md): Returns a single todo comment with its documents, creator and todo.

Permission: get:toDoComment

Errors: 404 when no comment with this id exists on your platform.

### Delete a todo comment

 - [DELETE /toDo/comment/{id}](https://docs.transportial.com/apis/todo/delete:todocomment.md): Soft-deletes a comment: it is flagged deleted and no longer listed on its todo.

Permission: delete:toDoComment

Errors: 404 when no comment with this id exists on your platform.

### List todos

 - [GET /board/lane/todos/{offset}/{amount}](https://docs.transportial.com/apis/todo/get:todos.md): Returns one page of the todos on your platform, most recently updated first. Archived and deleted todos are excluded.

Each todo carries its comment and attachment counts in statistics. Loading a todo whose deadline has passed adds the overDue status (unless it is already completed) and persists that change.

Permission: get:toDos

### Search todos

 - [POST /board/lane/todos/{offset}/{amount}](https://docs.transportial.com/apis/todo/search:todos.md): Full-text search over the todos of your platform, sorted by most recently updated first (then oldest created).

query is matched as a phrase (slop 10) against the todo's search text: name, description, type, the reporter's and assignee's first name, last name and username. An empty body or blank query returns every todo of the platform.

Loading a todo whose deadline has passed adds the overDue status (unless it is already completed) and persists that change.

Permission: search:toDos

### Get todo statistics

 - [GET /todo:statistics](https://docs.transportial.com/apis/todo/get:todo:statistics.md): Counts the todos of your platform in total and per status (todo, overDue, in_progress, completed). A todo with several statuses is counted under each of them.

Permission: get:toDo:statistics

### Update a todo comment

 - [PUT /toDo/comment](https://docs.transportial.com/apis/todo/update:todocomment.md): Merges the request body into the stored comment identified by the body id. Fields left out keep their stored value. Mentions are not notified again.

Permission: update:toDoComment

Errors: 404 when the comment belongs to another platform.

### Add a comment to a todo

 - [POST /toDo/comment](https://docs.transportial.com/apis/todo/create:todocomment.md): Adds a comment to the todo referenced by toDo.id.

The comment text is Draft.js raw content (JSON). Every user @mentioned in it, except the creator, receives a push notification on their registered devices and an in-app notification linking to the todo; mentions are resolved among the users of your platform only.

Permission: create:toDoComment

Errors: 404 when toDo.id is missing or the todo does not exist on your platform, or when the body carries the id of an existing comment.

### List comments of a todo

 - [GET /toDo/{id}/comments/{offset}/{amount}](https://docs.transportial.com/apis/todo/get:todocomments.md): Returns one page of the comments on a todo, oldest first. Deleted and archived comments are excluded from the page.

totalResults counts all comments of the todo, including deleted and archived ones.

Permission: get:toDoComments

Errors: 404 when the todo does not exist on your platform.

### Search comments of a todo

 - [POST /toDo/{id}/comments/{offset}/{amount}](https://docs.transportial.com/apis/todo/search:todocomments.md): Full-text search over the non-deleted, non-archived todo comments of your platform.

- The comments are limited to the todo in the path only when the body contains a toDo key; without it the search spans the comments of every todo on your platform.
- query is matched as a phrase (slop 10) against the comment text and the creator's name.
- sortBy orders the results; without it they are sorted by relevance.

Permission: get:toDoComments

Errors: 404 when the todo does not exist on your platform.

### List todos in a period

 - [GET /todos:byDateRange/{fromDate}/{toDate}](https://docs.transportial.com/apis/todo/get:todos:bydaterange.md): Returns all todos of your platform whose deadline or creation time falls between fromDate and toDate. Used by calendar views.

Not paginated: totalResults is the number of todos returned.

Permission: get:toDos

### Create a todo in a lane

 - [POST /board/lane/{id}/todo](https://docs.transportial.com/apis/todo/create:todo.md): Creates a todo in the given lane and returns the lane with its todos.

Behaviour:
- assignee is resolved by id among the users of your platform; an unknown id leaves the todo unassigned.
- The caller becomes the reporter.
- The todo receives the lane's todoStatus as its status, unless the lane's status is default.
- The todo is inserted at position (-1 or a position past the end appends it) and all todos in the lane are renumbered from 0.
- The assignee receives an in-app notification unless they created the todo themselves; users @mentioned in the description are notified as well.
- The AfterCreatingTodo integration trigger fires, plus AfterAssigningTodo when an assignee is set.

Permission: create:toDo

Errors: 404 when the lane does not exist on your platform.

### List a user's todos in a period

 - [GET /todos:byDateRange/user/{user}/{fromDate}/{toDate}](https://docs.transportial.com/apis/todo/get:todos:user:bydaterange.md): Returns the non-deleted todos of your platform that the user is assigned to or reported, with a deadline between fromDate and toDate.

Not paginated: totalResults is the number of todos returned.

Permission: get:toDos

Errors: 404 when the user does not exist on your platform.

### List todos linked to an entity

 - [GET /todos:byEntity/{entityType}/{entityId}/{offset}/{amount}](https://docs.transportial.com/apis/todo/get:todos:byentity.md): Returns one page of the todos whose relatedEntityType and relatedEntityId match the path, for example all todos attached to a trip.

totalResults is the number of todos on this page, not the total number of matches.

Permission: get:toDos

## Workflow

A workflow is a no-code automation: a graph of steps (trigger, condition, HTTP request, create/update/load entity, send message, delay, TQL query, set variable, log, validation, end) connected by edges. It starts on an entity trigger event, on an interval or cron schedule, or manually, and only runs automatically while enabled (status active). Every run is stored as a workflow execution with per-step input, output and duration; long delays park a run as waiting until the scheduler resumes it. A global catalog of templates, maintained by super admins, can be copied into any platform.

### List workflows

 - [GET /workflows/{offset}/{amount}](https://docs.transportial.com/apis/workflow/get:workflows.md): Returns one page of your platform's workflows, most recently updated first. Archived and deleted workflows and catalog templates are excluded.

Permission: get:workflow

### Search workflows

 - [POST /workflows/{offset}/{amount}](https://docs.transportial.com/apis/workflow/search:workflows.md): Searches your platform's workflows. Deleted and archived workflows and catalog templates are never returned. Results are ordered by relevance.

- query is matched as a phrase (slop 10) against name, description, status and template category.
- status keeps only workflows in that status.
- enabled keeps only enabled (true) or disabled (false) workflows.

Permission: get:workflow

### List executions of a workflow

 - [GET /workflow/{id}/executions/{offset}/{amount}](https://docs.transportial.com/apis/workflow/get:workflow:executions.md): Returns one page of the runs of a workflow, newest first, each with its step results.

Permission: get:workflow

Errors: 404 when the workflow does not exist on your platform.

### Get a workflow

 - [GET /workflow/{id}](https://docs.transportial.com/apis/workflow/get:workflow.md): Returns a single workflow of your platform with its steps, connections, filters and schedule.

Permission: get:workflow

Errors: 404 when no workflow with this id exists on your platform.

### Delete a workflow

 - [DELETE /workflow/{id}](https://docs.transportial.com/apis/workflow/delete:workflow.md): Soft-deletes a workflow: it is flagged deleted, stops reacting to triggers and schedules and is no longer listed. Deleting a catalog template requires a super admin.

Permission: delete:workflow

Errors: 401 when a non-super-admin deletes a catalog template; 404 when no workflow or template with this id exists.

### Run a workflow now

 - [POST /workflow/{id}/execute](https://docs.transportial.com/apis/workflow/execute:workflow.md): Runs the workflow synchronously with the request body as trigger data and returns the execution record.

The run starts at the trigger step and follows the connections. It ignores enabled, status, filters, maxConcurrentExecutions and the dedupe window, so a draft can be tested. A long delay step parks the run in waiting; the scheduler resumes it later. A failing step marks the execution failed with its errorMessage. The workflow's lastExecuted is updated.

Permission: execute:workflow

Errors: 404 when no workflow with this id exists on your platform.

### Copy a workflow template

 - [POST /workflow-template/{id}/duplicate](https://docs.transportial.com/apis/workflow/duplicate:workflow:template.md): Returns an editable copy of a catalog template for your platform without saving it. Save the copy with POST /workflow.

The copy gets a new id, is disabled, in draft status and not a template. Portal links and branding in its step configuration are replaced with your platform's own. Server-owned fields (uuid, timestamps, version, platform, lastExecuted, notes) are cleared.

Permission: create:workflow

Errors: 404 when no non-deleted, non-archived catalog template with this id exists.

### Duplicate a workflow

 - [POST /workflow/{id}/duplicate](https://docs.transportial.com/apis/workflow/duplicate:workflow.md): Saves a copy of one of your platform's workflows. The copy is named after the original with the suffix  (copy), disabled, in draft status and never a template.

Permission: create:workflow

Errors: 404 when no workflow with this id exists on your platform.

### Update a workflow

 - [PUT /workflow](https://docs.transportial.com/apis/workflow/update:workflow.md): Merges the request body into the stored workflow identified by the body id. Fields left out keep their stored value. The trigger step configuration and status are re-derived as on create.

Catalog templates (the stored row or the body has template: true) can only be edited by a super admin and always stay templates.

Permission: update:workflow; editing a catalog template additionally requires a super admin (role level 1000 or higher).

Errors: 400 when the schedule has an unparseable cron expression; 401 when a non-super-admin edits a template; 404 when the workflow belongs to another platform.

### Create a workflow

 - [POST /workflow](https://docs.transportial.com/apis/workflow/create:workflow.md): Creates a workflow on your platform. A missing or null id is replaced by a generated one.

On save:
- The trigger step's config (triggerType, triggerEvent, cronExpression, cronTimezone, dedupeSelector, dedupeMinutes) is copied onto the workflow's own trigger fields.
- status is derived from enabled: enabled workflows become active, a disabled active workflow becomes paused; archived is kept.
- With template: true the workflow is added to the global catalog instead: it gets no platform, stays disabled and in draft.

Permission: create:workflow; adding a catalog template (template: true) additionally requires a super admin (role level 1000 or higher).

Errors: 400 when the schedule has an unparseable cron expression; 401 when a non-super-admin creates a template.

### List workflow templates

 - [GET /workflow-templates](https://docs.transportial.com/apis/workflow/get:workflow:templates.md): Returns the global workflow catalog: ready-made workflows every platform can start from, ordered by template category and name. Deleted and archived templates are excluded. Not paginated.

Use POST /workflow-template/{id}/duplicate to load a template into your platform.

Permission: get:workflow

### Get a workflow execution

 - [GET /workflow/execution/{id}](https://docs.transportial.com/apis/workflow/get:workflow:execution.md): Returns a single workflow run with its trigger data, final context variables and step results.

Permission: get:workflow

Errors: 404 when no execution with this id exists on your platform.

## ValidationRule

Validation rules judge transport orders, consignments, trips and invoices with a yes/no rule when they are created, updated, change status or (invoices) are about to be sent. A rule is either deterministic (`filter`: filters on the entity's JSON, the same paths as message automations and workflows) or an AI judge (`aiJudge`: the platform's AI model answers a plain-language criterion; off by default, cached per rule and entity version, limited per day). Per channel (web app, portal, API and integrations) a violation warns or blocks: blocked saves answer `400` with `validation_rule_blocked`, warned web and portal saves answer `400` with `validation_rule_warning` until resent with `acknowledgeValidationWarnings=true`, and warned API saves go through, logged on the entity's activity and named in the `X-Validation-Warnings` header. Rules only judge; they never change data. Rules can be drafted by AI from a sentence, tested against recent entities before they are enabled, used by workflow `validation` steps and copied between platforms with the platform configuration copy.

### Update the validation rule settings

 - [PUT /validation-rules/settings](https://docs.transportial.com/apis/validationrule/update:validationrulesettings.md): Changes the AI judge switch, its daily call limit and maximum entity size, and the tester's default sample size. Fields left out keep their value; values out of range are clamped.

Permission: update:validationRule

### Update a validation rule

 - [PUT /validation-rule/{id}](https://docs.transportial.com/apis/validationrule/update:validationrule.md): Replaces a validation rule with the body (the whole rule is sent). Changing a rule also invalidates the AI judge's cached answers for it.

Permission: update:validationRule

Errors: 400 with validation_rule_invalid; 404 when the platform has no rule with this id.

### Delete a validation rule

 - [DELETE /validation-rule/{id}](https://docs.transportial.com/apis/validationrule/delete:validationrule.md): Removes a validation rule. Workflow validation steps that name it by id simply skip it from then on.

Permission: delete:validationRule

Errors: 404 when the platform has no rule with this id.

### List validation rules

 - [GET /validation-rules](https://docs.transportial.com/apis/validationrule/get:validationrules.md): Returns the platform's validation rules in evaluation order, the AI judge and tester settings, and the status values onStatusChange rules can react to per entity type.

Permission: get:validationRules

### Create a validation rule

 - [POST /validation-rules](https://docs.transportial.com/apis/validationrule/create:validationrule.md): Adds a validation rule: a yes/no judgement on transport orders, consignments, trips or invoices when they are created, updated, change status or (invoices) are about to be sent. In filter mode requirement must hold whenever appliesWhen holds; in aiJudge mode the platform's AI model judges aiInstruction (only with aiJudgeEnabled). Per channel (web, portal, api) a violation is off, warn or block: a blocked save answers 400 with validation_rule_blocked, a warned web/portal save answers 400 with validation_rule_warning until it is resent with acknowledgeValidationWarnings=true, and a warned API or integration save goes through, logged on the entity's activity and listed in the X-Validation-Warnings response header. Rules never change the entity. New rules usually start with enabled: false so they can be tested first.

Permission: create:validationRule

Errors: 400 with validation_rule_invalid (params.field) for a rule without name, trigger or condition, an unknown status, or beforeInvoiceSend on a non-invoice rule; at most 200 rules per platform.

### Draft a validation rule from plain language

 - [POST /validation-rules/draft](https://docs.transportial.com/apis/validationrule/create:validationruledraft.md): Asks the platform's AI model to turn a requirement in plain language (for example "orders for customer Acme need a reference starting with PO") into a validation rule. It drafts deterministic filters on the entity's field paths when it can, and an aiJudge rule when the requirement needs judgement. Nothing is saved: the draft comes back disabled, to test with POST /validation-rules/test and save with POST /validation-rules. One AI call per request.

Permission: create:validationRule

Errors: 400 with validation_rule_invalid for an empty or too long prompt.

### Test a validation rule on recent entities

 - [POST /validation-rules/test](https://docs.transportial.com/apis/validationrule/test:validationrule.md): Runs a rule (saved or not, enabled or not) against the platform's most recently created entities of its type and reports per entity whether it is in scope and would pass. Triggers and severities are ignored; nothing is saved or changed. aiJudge rules check at most 10 entities and need aiJudgeEnabled; their answers are cached and count towards the daily limit.

Permission: test:validationRule

Errors: 400 with validation_rule_invalid, or validation_rule_ai_judge_disabled for an aiJudge rule while the AI judge is off.

## Dashboard

A dashboard is a user-composed grid of widget tiles, each showing one of the platform's views or tools (planboard, map, notifications, insights). It belongs to the user who created it and stays private unless shared, in which case everyone on the platform can open and copy it, and platform admins can also edit it. Each user can mark one dashboard as their home dashboard. The feature is behind the customDashboards platform flag.

### Get a dashboard

 - [GET /dashboard/{id}](https://docs.transportial.com/apis/dashboard/get:dashboard.md): Returns a dashboard the caller owns, or a dashboard shared on your platform.

Permission: any authenticated user on a platform with the customDashboards feature enabled (a user-level override is honoured); access is decided by ownership, not by a named permission.

Errors: 404 when the dashboard does not exist on your platform or is another user's private dashboard.

### Delete a dashboard

 - [DELETE /dashboard/{id}](https://docs.transportial.com/apis/dashboard/delete:dashboard.md): Soft-deletes a dashboard and clears its home-dashboard flag. Allowed for the owner, and for platform admins (role level 800 or higher) on shared dashboards.

Permission: any authenticated user on a platform with the customDashboards feature enabled (a user-level override is honoured); access is decided by ownership, not by a named permission.

Errors: 401 when the caller may see but not edit the dashboard; 404 when the dashboard does not exist or is another user's private dashboard.

### Copy a dashboard

 - [POST /dashboard/{id}/duplicate](https://docs.transportial.com/apis/dashboard/duplicate:dashboard.md): Saves a private copy of a dashboard the caller can see (their own or a shared one), owned by the caller. The copy is not shared and not the home dashboard. An optional body name names the copy; otherwise it keeps the source's name.

Permission: any authenticated user on a platform with the customDashboards feature enabled (a user-level override is honoured); access is decided by ownership, not by a named permission.

Errors: 404 when the dashboard does not exist or is another user's private dashboard.

### Update a dashboard

 - [PUT /dashboard](https://docs.transportial.com/apis/dashboard/update:dashboard.md): Replaces the name, description, shared, columns and layout of the dashboard identified by the body id. The owner (user) never changes. Tiles are normalised as on create. Only the owner can change isDefault; setting it clears the flag on their other dashboards.

The owner may edit their dashboard; platform admins (role level 800 or higher) may also edit shared dashboards.

Permission: any authenticated user on a platform with the customDashboards feature enabled (a user-level override is honoured); access is decided by ownership, not by a named permission.

Errors: 400 when name is blank; 401 when the caller may see but not edit the dashboard; 404 when the dashboard does not exist or is another user's private dashboard.

### Create a dashboard

 - [POST /dashboard](https://docs.transportial.com/apis/dashboard/create:dashboard.md): Creates a dashboard owned by the caller. The body's user and id are ignored. The name is trimmed. Tiles without a type are dropped and every tile is clamped into the grid. With isDefault: true it becomes the caller's home dashboard and their other dashboards lose that flag.

Permission: any authenticated user on a platform with the customDashboards feature enabled (a user-level override is honoured); access is decided by ownership, not by a named permission.

Errors: 400 when name is blank.

### List dashboards visible to the caller

 - [GET /dashboards](https://docs.transportial.com/apis/dashboard/get:dashboards.md): Returns every dashboard the caller may open on your platform: their own dashboards plus the dashboards other users shared. The caller's home dashboard comes first, then by creation time. Not paginated.

Permission: any authenticated user on a platform with the customDashboards feature enabled (a user-level override is honoured); access is decided by ownership, not by a named permission.

## Insights

Insights are read-only reports over the platform's operational and financial data: trips, stops and legs per vehicle, driver or fleet, CO2e and fuel/idling cost, temperature compliance, tachograph activities, revenue, open-invoice aging, quote conversion, lanes and user activity. Each report returns typed tables (rows of `Value` cells, optionally with a totals row) that the UI renders and exports to Excel. Several reports share the generator that also produces scheduled report e-mails. The insights AI chat answers natural-language questions by running read-only, platform-scoped TQL queries and returns those queries with the answer.

### Get the loading sheet of a location

 - [POST /insights/trips:loading/location/{id}](https://docs.transportial.com/apis/insights/get:tripsloadingsheet.md): Loading sheet for one location: the trips starting in the period that stop at this location (cancelled trips excluded), grouped into one table per vehicle (named after its license plate). Each row is a stop at the location with trip, vehicle, fleet number at the time of the trip, start date, consignment number, reference, transport order, last action location and the goods (type, quantity, description).

startDate is required; endDate defaults to startDate. Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:tripsLoadingSheet

Errors: 404 when no live location with this id exists on your platform; a missing or unparsable startDate/endDate fails the request.

### Report temperature compliance per vehicle

 - [POST /insights/vehicles/temperatureCompliance:byDateRange](https://docs.transportial.com/apis/insights/get:insights:vehiclestemperaturecompliancebydaterange.md): Per vehicle and cargo temperature sensor over the period: the allowed band, number of samples, minimum, maximum and average temperature (deg C), the number of samples outside the band and the compliance percentage. The band is the sensor's own minimum/maximum constraint when set, otherwise the platform's default band (FMS settings); either side may be open.

vehicles lists vehicle ids; an empty list means every vehicle of your platform, and ids that do not resolve on your platform are skipped.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:vehicle

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report border crossings per driver

 - [POST /insights/chauffeurs/borderCrossings:byDateRange](https://docs.transportial.com/apis/insights/get:insights:chauffeurbordercrossingbydaterange.md): Per driver, every moment a vehicle the driver was attached to entered another country in the period, derived from the vehicle's position updates: license plate, vehicle, coordinates, country, date and time. Attach events without a vehicle are skipped. One table per driver, named after the driver.

chauffeurs lists driver ids; an empty list means every driver of your platform.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:chauffeurBorderCrossingByDateRange

Errors: 400 when the body is not valid JSON or a date is invalid; 404 when one of the given driver ids does not exist on your platform.

### Report quote conversion per customer

 - [POST /insights/quotes/conversion:byDateRange](https://docs.transportial.com/apis/insights/get:insights:quoteconversionbydaterange.md): Quotes in the period per customer: number of quotes, won, lost and the resulting conversion percentage, with a total row. Rendered by the shared report generator that also feeds scheduled report e-mails. startDate/endDate default to the last 30 days when omitted.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:quoteConversionByDateRange

Errors: 400 when the body is not valid JSON. A failing report returns success = false with an empty table.

### Report idling time and fuel cost per vehicle

 - [POST /insights/vehicles/idlingFuelCost:byDateRange](https://docs.transportial.com/apis/insights/get:insights:vehiclesidlingfuelcostbydaterange.md): Per vehicle over the period: distance (km), idling time (minutes, from the driving-style analysis of rpm and speed), fuel used (litres, from the cumulative fuel counter), litres per 100 km, fuel cost, and the estimated idling fuel and idling cost. Prices use the platform's FMS settings (fuel price per litre, default 1.80; idle burn rate, default 3.0 l/h). Counter-based values are empty when fewer than two samples exist or the counter reset.

vehicles lists vehicle ids; an empty list means every vehicle of your platform, and ids that do not resolve on your platform are skipped.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:vehicle

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report finished transport orders that are not invoiced

 - [POST /insights/transportOrders/uninvoiced:byDateRange](https://docs.transportial.com/apis/insights/get:insights:uninvoicedworkbydaterange.md): Realized transport orders completed in the period (end of their last trip, else of their last executed action) that have no sent sales invoice (open, paid, reminded or too_late), oldest first: order, customer reference, customer, completion, days waiting, expected revenue, whether a draft invoice exists and, with active required-document rules, whether missing required documents block or warn on invoicing and which. Rendered by the shared report generator that also feeds scheduled report e-mails. startDate/endDate default to the last 30 days when omitted.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:uninvoicedWorkByDateRange

Errors: 400 when the body is not valid JSON. A failing report returns success = false with an empty table.

### Report the tachograph activity log

 - [POST /insights/chauffeurs/tachoActivities:byDateRange](https://docs.transportial.com/apis/insights/get:insights:tachoactivitiesbydaterange.md): The tachograph activity log of the whole fleet in the period: one row per activity (date, start, end, duration, activity, driver, vehicle, start and end location) in driver-then-time order, with a totals row per activity type. Places are named after the fleet's own geofences when the position falls inside one. Times are rendered in the platform's time zone. Rendered by the shared report generator that also feeds scheduled report e-mails. startDate/endDate default to the last 30 days when omitted.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:chauffeur

Errors: 400 when the body is not valid JSON. A failing report returns success = false with an empty table.

### Report open outbound invoices by age

 - [POST /insights/invoices/openAging](https://docs.transportial.com/apis/insights/get:insights:openinvoicesaging.md): Every outbound invoice of your platform that still has an open amount (status open, reminded or too late), oldest due date first: invoice, customer, invoice date, due date, days overdue and aging bucket (Not yet due, 1-30 days, 31-60 days, 61-90 days, 90+ days), with a total row per currency. Computed at the moment of the request; no body is read.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:openInvoicesAging

Errors: none specific. A failing report returns success = false with an empty table.

### Report consignments created in a period

 - [POST /insights/consignments:results](https://docs.transportial.com/apis/insights/get:consignmentresultsbydate.md): Every consignment of your platform created in the period (deleted and archived ones excluded) with its number, type, reference description, the locations of its first and last action, and its goods (type and quantity).

startDate is required; endDate defaults to startDate. Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:consignmentResultsByDate

Errors: a missing or unparsable startDate/endDate fails the request (400 for invalid JSON).

### Report trips per driver

 - [POST /insights/chauffeurs/trips:byDateRange](https://docs.transportial.com/apis/insights/get:insights:tripsbychauffeurbydaterange.md): Trips starting in the period per driver (trips without a driver under No driver assigned): number of trips and completed trips, most trips first, with a total row. A trip with several drivers counts for each of them. Rendered by the shared report generator that also feeds scheduled report e-mails. startDate/endDate default to the last 30 days when omitted.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:tripsByChauffeurByDateRange

Errors: 400 when the body is not valid JSON. A failing report returns success = false with an empty table.

### Report the trips of several vehicles

 - [POST /insights/vehicles/trips](https://docs.transportial.com/apis/insights/get:insights:vehiclestripsbydaterange.md): One table per vehicle with its trips of the vehicle (cancelled trips excluded) whose start, end or estimated end falls in the period: trip, status, license plate, fleet number at the time of the trip, first origin, final destination, the move legs, distance, duration, costs and cost split, start and end date/time.

vehicles lists vehicle ids; an empty list means every vehicle of your platform, and ids that do not resolve on your platform are skipped.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:vehiclesTripByDateRange

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report stop punctuality against agreed time windows

 - [POST /insights/stops/onTime:byDateRange](https://docs.transportial.com/apis/insights/get:insights:ontimeperformancebydaterange.md): Executed stops in the period (by actual start) against their agreed time windows, per groupBy: CUSTOMER (default), LOCATION, CARRIER (own fleet or the carrier of a forwarded trip) or DRIVER. Columns: stops, stops with a date window, on time, too late, too early, on-time percentage (of the stops with a window), average and maximum minutes late, with a total row. The verdict is the stop's own timeliness assessment (planStatus, tolerances from the planning settings) re-evaluated on its actual start; opening hours alone do not count as a window. A stop with several customers or drivers counts for each. Rendered by the shared report generator that also feeds scheduled report e-mails. startDate/endDate default to the last 30 days when omitted.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:onTimePerformanceByDateRange

Errors: 400 when the body is not valid JSON. A failing report returns success = false with an empty table.

### Report CO2e emissions per customer

 - [POST /insights/customers/emissions:byDateRange](https://docs.transportial.com/apis/insights/get:insights:customeremissionsbydaterange.md): Customer-facing emission report (CSRD): well-to-wheel (WTW) and tank-to-wheel (TTW) CO2e in kg per customer, for the trips created in the period. Uses the emissions already stored on each trip (from the vehicle's fuel type and emission standard); nothing is recalculated. A trip's emissions are split equally over the customers of its transport orders; trips without a customer go to Unattributed. Rows are sorted by WTW descending, with a total row that reconciles to the sum of the trip emissions.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:customerEmissionsByDateRange

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report CO2 emissions per vehicle

 - [POST /insights/vehicles/CO2:byDateRange](https://docs.transportial.com/apis/insights/get:insights:vehiclesco2bydaterange.md): Per vehicle: name, license plate, fuel type, odometer at the start and end of the period, the distance in km and the CO2 produced in kg, calculated from the fuel type and emission standard (Euro 6 when unknown).

vehicles lists vehicle ids; an empty list means every vehicle of your platform, and ids that do not resolve on your platform are skipped. type is accepted but not used.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:vehiclesCO2ByDateRange

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report the trips of a fleet

 - [GET /insights/fleet/{id}/trips/{startDate}/{endDate}](https://docs.transportial.com/apis/insights/get:insights:fleettripsbydaterange.md): For a fleet, a Summary table (fleet number, license plate, total kilometres and costs of completed trips) followed by one table per fleet position with its trips in the period: trip, status, license plate, fleet number, first origin, final destination, the move legs, distance, duration, costs and cost split, start and end date/time. A trip counts when its start, end or estimated end falls in the period (cancelled trips excluded); the fleet's composition is followed through its revision history.

When no fleet with this id exists on your platform the result is empty. Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:insight or get:fleetTripsByDateRange

Errors: an unparsable startDate/endDate fails the request.

### Get the welcome dashboard counters

 - [GET /insights/user/welcome-stats](https://docs.transportial.com/apis/insights/get:insights:welcomestats.md): The counters of the welcome dashboard for the signed-in user: open actions on the platform (life cycle planned, projected or actual), consignments in transit, and the user's unread chat messages (the same total as the chat badge).

Permission: get:insights

Errors: 404 when the session has no user.

### Report transport orders per customer

 - [POST /insights/customer/transportOrders:byDateRange](https://docs.transportial.com/apis/insights/get:insights:percustomertransportordersbydaterange.md): The number of transport orders per customer in the period, limited to orders in the given status (default REALIZED), with a total row. startDate defaults to one month ago and endDate to now.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:insight

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report the trips of one vehicle

 - [GET /insights/vehicle/{id}/trips/{startDate}/{endDate}](https://docs.transportial.com/apis/insights/get:insights:vehicletripsbydaterange.md): The trips of the vehicle (cancelled trips excluded) whose start, end or estimated end falls in the period, oldest first: trip, status, license plate, fleet number at the time of the trip, first origin, final destination, the move legs (from, to, start, end), distance, duration, costs and cost split, start and end date/time.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:vehicleTripsByDateRange

Errors: 404 when no live vehicle with this id exists on your platform; an unparsable startDate/endDate fails the request.

### Report invoiced revenue per customer

 - [POST /insights/customers/revenue:byDateRange](https://docs.transportial.com/apis/insights/get:insights:revenuebycustomerbydaterange.md): Outbound invoices dated in the period (status open, paid, reminded or too late) per customer: number of invoices, invoiced amount and open amount, highest revenue first, with a total row. Rendered by the shared report generator that also feeds scheduled report e-mails. startDate/endDate default to the last 30 days when omitted.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:revenueByCustomerByDateRange

Errors: 400 when the body is not valid JSON. A failing report returns success = false with an empty table.

### Report the move legs of a fleet

 - [GET /insights/fleet/{id}/moveActions/{startDate}/{endDate}](https://docs.transportial.com/apis/insights/get:insights:fleetmoveactionsbydaterange.md): For a fleet, a Summary table (fleet number, license plate, total kilometres and costs of completed trips) followed by one table per fleet position with every move leg of its trips in the period: trip, transport mode, vehicle, customers, drivers, statuses, from/to, odometer start/end, distance, duration, costs and cost split, start and end date/time. The fleet's composition is followed through its revision history, so a vehicle only counts for the time it was part of the fleet.

When no fleet with this id exists on your platform the result is empty. Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:insight or get:fleetMoveActionsByDateRange

Errors: an unparsable startDate/endDate fails the request.

### Report load and unload legs per driver

 - [POST /insights/chauffeurs/actions:byDateRange](https://docs.transportial.com/apis/insights/get:insights:chauffeursactionsbydaterange.md): Per driver, the move legs driven while the driver was attached to a vehicle in the period (attach/detach events created in the period), with load and unload date, time and country. The last column classifies each leg against the country of the vehicle's business: K = cabotage (domestic transport in another country), C = cross-trade (between two foreign countries), otherwise T. One table per driver, named after the driver.

chauffeurs lists driver ids; an empty list means every driver of your platform.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:chauffeursActionsByDateRange

Errors: 400 when the body is not valid JSON or a date is invalid; 404 when one of the given driver ids does not exist on your platform.

### Report revenue, cost and margin

 - [POST /insights/margin:byDateRange](https://docs.transportial.com/apis/insights/get:insights:marginbydaterange.md): Expected (quoted and priced) and invoiced revenue, cost and margin, with margin percentages, from the ledger for the work of the non-cancelled trips that start in the period, per groupBy: ORDER (default, the transport orders those trips carry), CUSTOMER, LANE (per consignment, UN/LOCODE or country plus postal area at both ends) or TRIP. Each entity counts its share of every ledger row, so trip costs spread over the orders they carry; amounts are net, and several currencies add up in the base currency of the rows' rate snapshots. Needs the financial feature, which keeps the ledger. Rendered by the shared report generator that also feeds scheduled report e-mails. startDate/endDate default to the last 30 days when omitted.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:marginByDateRange

Errors: 400 when the body is not valid JSON. A failing report returns success = false with an empty table.

### Report a sensor value per vehicle at a moment

 - [POST /insights/vehicles/sensorValueAtDate](https://docs.transportial.com/apis/insights/get:insights:vehiclessensorvalueatdate.md): For each vehicle, the value of one sensor type (for example odoMeters or temperature) taken from the reading closest in time to date (before or after), one row per matching sensor: vehicle, value, unit, date and time of the reading.

vehicles lists vehicle ids; an empty list means every vehicle of your platform. Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:vehiclesSensorValueAtDate

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report which vehicles each driver drove

 - [POST /insights/chauffeurs/onTruck:byDateRange](https://docs.transportial.com/apis/insights/get:insights:chauffeursontruckbydaterange.md): Per driver, every attach to a vehicle in the period: vehicle name, license plate, fleet, how long the driver stayed on it, and the date and time of the attach. One table per driver, named after the driver.

chauffeurs lists driver ids; an empty list means every driver of your platform.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:chauffeursOnTruckByDateRange

Errors: 400 when the body is not valid JSON or a date is invalid; 404 when one of the given driver ids does not exist on your platform.

### Report activity per user

 - [POST /insights/users/report](https://docs.transportial.com/apis/insights/get:insights:usersreport.md): Activity per user of your platform in the period: orders created, trips finished, average trip distance, trips finished on time and too late, messages sent and sessions (logins).

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:insight

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report vehicle utilization

 - [POST /insights/vehicles/utilization:byDateRange](https://docs.transportial.com/apis/insights/get:insights:vehicleutilizationbydaterange.md): How much of the period each vehicle was on a trip: number of trips starting in the period, active days and driven hours per vehicle, with a total row. Rendered by the shared report generator that also feeds scheduled report e-mails. startDate/endDate default to the last 30 days when omitted.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:vehicleUtilizationByDateRange

Errors: 400 when the body is not valid JSON. A failing report returns success = false with an empty table.

### Ask the insights AI assistant

 - [POST /insights/chat](https://docs.transportial.com/apis/insights/post:insightchat.md): One turn of the insights AI chat. Send the whole conversation so far (the server keeps no state); the platform's configured AI model answers in natural language and may run up to 8 read-only TQL queries on your platform's data while doing so (each capped at 200 rows). Every query it ran is returned with its rows, so the answer can be checked.

The assistant also answers how-to questions from the public help center (cited in helpArticles), finds orders, consignments, trips and vehicles by reference (records, only types the caller may read) and explains failed imports from the stored integration and document inbox errors (needs get:integrationLogs / get:documentIntelligence).

Permission: get:insight

Errors: 400 when the body is not valid JSON.

### Report trips per vehicle per day

 - [POST /insights/trips:dailyReport](https://docs.transportial.com/apis/insights/get:insights:tripsdailyreport.md): One table per vehicle (named license plate - name) with its trips of the vehicle (cancelled trips excluded) whose start, end or estimated end falls in the period, ordered by start: trip number, date, drivers, vehicle, number of stops, load and unload stops, duration, distance, total loading metres and total weight (in the platform's units).

vehicles lists vehicle ids; an empty list means every vehicle of your platform, and ids that do not resolve on your platform are skipped.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:tripsDailyReport

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report the stops of vehicles

 - [POST /insights/vehicles/actions](https://docs.transportial.com/apis/insights/get:insights:vehiclesactionsbydaterange.md): One table per vehicle with the stops of its trips of the vehicle (cancelled trips excluded) whose start, end or estimated end falls in the period: sequence, actions, consignment references and names, location name and address, expected arrival and departure, travel time and kilometres, references, remarks, totals (collo, transport equipment, loading metres, kg) and start/end time.

vehicles lists vehicle ids; an empty list means every vehicle of your platform, and ids that do not resolve on your platform are skipped.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:vehiclesActionsByDateRange

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

### Report transported lanes and their volumes

 - [POST /insights/consignments/lanes:byDateRange](https://docs.transportial.com/apis/insights/get:insights:laneanalysisbydaterange.md): The lanes (origin to destination) transported in the period with the number of consignments per lane and their share, with a total row. Rendered by the shared report generator that also feeds scheduled report e-mails. startDate/endDate default to the last 30 days when omitted.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:laneAnalysisByDateRange

Errors: 400 when the body is not valid JSON. A failing report returns success = false with an empty table.

### Report actions and/or events in a period, sorted on time

 - [POST /insights/timeline:byDateRange](https://docs.transportial.com/apis/insights/get:insights:timelinebydaterange.md): One table with a row per action and/or event in the period, oldest first: time, kind (action/event), type, trip, vehicle, transport mode, drivers, location, address, consignment, customer reference, ETA, ETD, start time, end time, status, plan status and remark.

Actions are the stops of the non-cancelled trips in the period plus their sub-actions (load, unload, ...). Each is placed by sortBy — eta or startTime — and a sub-action without that time takes its stop's; actions without it are left out. Events are placed by their dateTime; eventTypes picks the types (empty = all except sensor and location updates).

vehicles lists vehicle ids; an empty list means the whole platform. At most limit rows (max 50000) are returned.

Page-less: the whole report is returned at once as rows of typed cells (Values) that the UI renders as a table and can export to Excel.

Permission: get:insight or get:timelineByDateRange

Errors: 400 when the body is not valid JSON or a date is not a valid ISO-8601 instant.

## Management

Management dashboards that combine figures from several modules for a period. The financial overview of one administration sums actual revenue, cost and result from invoices, quoted revenue from quotes and the expected cost of the trip pipeline. The operational overview reports net profit, driven and empty distance, revenue per metre, consignment and trip counts and trip execution state, plus a per-day series of trips and distance. Figures are computed on request and are read-only; a failed computation returns zeros.

### Get the financial overview of an administration

 - [GET /management/financial/{administrationId}/{start}/{end}](https://docs.transportial.com/apis/management/get:management:financial.md): Key financial figures of one administration of your platform for a period, for the management dashboard:

- actual figures from invoices dated in the period with status OPEN, PAID, REMINDED or TOO_LATE: revenue (outgoing invoices), open revenue (outgoing, not yet paid), cost (incoming invoices) and result (revenue minus cost);
- the outgoing invoices and the quotes of the administration dated or created in the period;
- quoted revenue from quotes dated in the period: total, accepted (status OPEN or ACCEPTED) and declined (DECLINED or CANCELLED);
- the pipeline: number of trips starting in the period that are requested or accepted, confirmed, or in transit, each with the average expected cost per trip.

Amounts are sums of line totals without currency conversion. teamId and customerId only narrow the trip (pipeline) figures, not the invoice and quote figures. When a query fails the error is logged and an overview with all figures zero is returned. Read-only.

Permission: get:management:finance

Errors: 404 when the administration does not exist on your platform; an unparseable start or end fails the request with a server error.

### Get the operational overview

 - [GET /management/operational/{start}/{end}](https://docs.transportial.com/apis/management/get:management:operational.md): Key operational figures of your platform for a period, for the management dashboard: net profit (outgoing minus incoming invoices dated in the period with status OPEN, PAID, REMINDED or TOO_LATE) and net profit per trip, driven and empty distance, average distance per vehicle, revenue per metre, number of consignments created and trips starting in the period, and trips by execution state (completed with plan status OK, not completed with plan status OK, too late, and not completed while off route).

Trips count when their start date falls in the period. The optional filters narrow trips and consignments through their transport orders; administrationId also narrows the invoice figures. When a query fails the error is logged and an overview with all figures zero is returned. Read-only.

Permission: get:management:operational

Errors: an unparseable start or end fails the request with a server error.

### Get the demand forecast

 - [GET /management/forecast/demand](https://docs.transportial.com/apis/management/get:management:forecast:demand.md): Forecast of transport demand for the coming weeks, with the history it is based on, for the management dashboard. Demand is counted per consignment on its requested pickup day (the earliest time window of its first pickup stop, else that stop's start time, else the order's creation day) in the platform's time zone. Orders that are templates, concepts, declined or cancelled, and cancelled or concept consignments, are left out.

The forecast combines a weekday profile, national public holidays, yearly seasonality (from 60 weeks of history) and a damped trend, and never drops below demand already booked. accuracy scores the method on the most recent weeks of history. A consignment moved by several transport modes counts for each of them; unplanned consignments take the mode their lane moved by most often.

With a modality and a measurable metric the forecast is compared with the capacity of that mode: own operational assets (vehicles, and for road also own trailers pulled by tractor units, divided by cycleDays, minus blocking calendar days) plus capacity contracts whose unit matches the metric. Read-only.

Permission: get:management:operational.

### Get trips and distance per day

 - [GET /management/operational/tripsPerDay/{start}/{end}](https://docs.transportial.com/apis/management/get:management:operational:tripsperday.md): Per calendar day (of the trip start date) in the period: the number of trips of your platform that start that day and their summed route distance, plus the empty distance, for the management dashboard chart. Days without trips are left out; days are ordered ascending.

Only trips with a route are counted. The optional filters narrow the trips through their transport orders. When the query fails the error is logged and an empty list is returned. Read-only.

Permission: get:management:operational

Errors: an unparseable start or end fails the request with a server error.

## Integration

An Integration connects a platform to an external system. Its type decides how: `manual` (a built-in connector such as Transics or Webfleet, with credentials and enabled functions), `configured` (user-built data sources with field mappings, started by entity events, a schedule or incoming calls), `fileTemplate` (maps documents with a known layout onto entities), `messageAutomation`, or `apiAccess` (a key pair for calling this API). Every unit of work is stored as an integration object, handled by tasks that write logs and redacted HTTP request logs; these can be listed with shared filters and traced as one run. Incoming data arrives through public webhook URLs (per connector listener or per web-hook data source, authenticated by the data source's credentials or an IP allow-list) and through inbound email. Integrations can be tested with saved test cases, drafted with AI, and are soft-deleted.

### Update an integration test

 - [PUT /integration/{id}/test/{testId}](https://docs.transportial.com/apis/integration/update:integration:test.md): Replaces the definition of a saved test case. Blank name, description, input and expectedOutput keep their stored values, and an absent or unknown dataSourceId keeps the stored data source. assertions, validationMode, the notification toggles and the recipient lists are taken as sent, so an empty list clears them and null toggles fall back to the platform defaults. The last run result is kept.

Permission: update:integration

Errors: 400 when the body cannot be parsed; 404 when no test with this id exists on your platform.

### Delete an integration test

 - [DELETE /integration/{id}/test/{testId}](https://docs.transportial.com/apis/integration/delete:integration:test.md): Permanently removes a saved test case.

Permission: delete:integration

Errors: 404 when no test with this id exists on your platform.

### List integrations

 - [GET /integrations/{offset}/{amount}](https://docs.transportial.com/apis/integration/get:integrations.md): Returns one page of the integrations of your platform. Deleted and archived integrations are not included.

Each item is serialised as its concrete integration type (manual, configured, fileTemplate, messageAutomation or apiAccess), selected by the type property.

Pagination: offset and amount are path segments. totalResults holds the total number of integrations, independent of the page.

Permission: get:integrations

Errors: 400 when offset is not a number.

### Search integrations

 - [POST /integrations/{offset}/{amount}](https://docs.transportial.com/apis/integration/search:integrations.md): Full-text search over the integrations of your platform. Deleted and archived integrations are never returned.

All body properties are optional and combined with AND:
- query matches the integration's search text (name, description and similar) as a phrase with a slop of 10;
- types restricts to integration types (manual, configured, fileTemplate, messageAutomation, apiAccess);
- manualType / manualTypes restrict manual integrations to one or more connectors (for example transics);
- sectorType restricts to one sector (for example boardComputer).

An empty body ({}) returns every integration. If the search index fails, an empty page is returned instead of an error.

Pagination: offset and amount are path segments; amount is passed to the search index as is (not clamped). totalResults holds the total number of hits.

Permission: get:integrations

Errors: 400 when the body is not valid JSON or offset/amount is not a number.

### Receive a webhook for a connector listener

 - [GET /integration/{id}/{token}](https://docs.transportial.com/apis/integration/handle:integration:webhook.md): Public endpoint an external system calls to push data to a manual (connector) integration. Accepts POST, PUT, GET and DELETE.

{token} is the name of the connector listener to run (for example ReceiveX12 for X12, ReceiveUpdates for WhatsApp), matched case-insensitively against the listeners of the integration's connector.

Authentication: none on the Transportial side beyond knowing the integration id and listener name. Provider-specific verification (signatures, verify tokens) is done by the listener itself where the provider offers it. A listener that requires a shared secret (Shippeo: its configured inbound header and secret) is checked before anything is stored; a failed check answers 401.

Behaviour:
1. The raw body is always stored as an integration object first, so nothing is lost.
2. If the integration is disabled, the object is marked ERROR and 405 is returned.
3. Otherwise a webhook task is created. If every matching listener is asynchronous, the object is queued for the listener job and 201 "Queued for processing" is returned immediately. Otherwise the listeners run inline; a listener may write its own response (for example a provider handshake), else 201 "Successfully handled" is returned.
4. If handling throws, the object is scheduled for retry, the error is logged on the integration and 500 is returned.

Permission: Public, no authentication.

Errors: 401 when the listener refuses the call; 404 when the integration does not exist; 405 when it is disabled; 500 when handling failed.

### Run an integration test

 - [POST /integration/{id}/test/{testId}/run](https://docs.transportial.com/apis/integration/run:integration:test.md): Runs one saved test case: its input is fed through the test's data source mapping (nothing is created) and the output is judged on the expected output and/or assertions, as set by validationMode. The test's lastStatus, lastResultMessage and lastRunAt are updated, and failure notifications are sent according to the test and platform settings.

A test without a data source, or whose mapping throws, is reported as failed rather than as an error.

Permission: get:integration

Errors: 404 when no test with this id exists on your platform.

### Set the OpenID token of an integration

 - [PUT /integration/{id}/openId](https://docs.transportial.com/apis/integration/update:integration:openidtoken.md): Completes the OpenID authorisation flow of a manual integration: stores the token returned by the provider on the integration's credentials and lets the connector exchange it (for example for access and refresh tokens), after which the integration can authenticate.

The token is read from the JSON body property token. The integration is looked up within the platform of the calling session.

Permission: update:integration

Errors: 403 without a session; 401 without the permission; 404 when no integration with this id exists on the caller's platform. A response with success = false and code = 400 (HTTP status 200) when the integration is not a manual integration, or its authentication method is not openId or clientKeysOpenId.

### Preview a cron schedule

 - [POST /integration/schedule/preview](https://docs.transportial.com/apis/integration/preview:integration:schedule.md): Checks a cron expression and returns the next moments it would fire, without saving anything. Use it to show what an expression means before it is stored on an integration schedule.

Dialect: 5 fields (minute hour day-of-month month day-of-week) are read as Unix cron, so 0 6   1-5 is Monday to Friday 06:00; 6 or 7 fields are read as Quartz cron (with seconds and optional year, supporting L, W and #). The macros @hourly, @daily, @midnight, @weekly, @monthly, @yearly and @annually are expanded.

The runs are computed in cronTimezone; a blank or unknown zone falls back to UTC, and timezone in the response says which zone was used. count is clamped to 1..20. An invalid expression returns valid = false and no runs, not an error.

Permission: get:integrations

Errors: 400 when the body is not valid JSON.

### Test a data source mapping

 - [POST /integration/dataSource/mapping:test](https://docs.transportial.com/apis/integration/test:integration:datasourcemapping.md): Runs a sample payload through an unsaved data source configuration and returns the result, without creating or changing anything.

The payload is parsed (JSON, XML, CSV, X12, EDIFACT or base64 file) and checked against the data source's filters. When it passes, an OTM data source returns the payload serialised as OTM entities; any other data source returns the JSON produced by its field mapping. When the filters reject the payload the result is empty.

Permission: create:integration

Errors: 400 when the body cannot be parsed.

### Generate integration credentials

 - [POST /integration/generate/credentials](https://docs.transportial.com/apis/integration/generate:integration:credentials.md): Generates fresh secrets for the authentication method in the body and returns the credentials; nothing is stored. Use it to issue credentials that an external system must send when it calls one of your incoming webhook data sources.

Per authenticationMethod:
- basic: a new UUID username and a 40-character password;
- apiKey: a 100-character apiKey;
- bearer: a signed JWT bearer token (a 100-character random value if signing fails);
- accessKeys: a UUID accessKey and a 100-character secretKey.

Other methods are returned unchanged. All other properties of the body are echoed back.

Permission: generate:integration:credentials

Errors: 400 when the body cannot be parsed.

### Update an integration

 - [PUT /integration](https://docs.transportial.com/apis/integration/update:integration.md): Updates an existing integration. The body must carry the integration's id; the stored integration is merged with the properties sent.

Side effects: every task of this integration that is still queued or processing is marked as ERROR, so work started under the old configuration does not continue. The same save-time rules as on create apply (cron validation, webhook token generation, connector setup, fingerprints).

Permission: update:integration

Errors: 400 when the body cannot be parsed or the cron expression is invalid; 400 with message integration_manual_type_unavailable when the update would create an integration on, or switch one to, a connector that is not offered for new integrations; 404 when no integration with the given id exists on your platform.

### Create an integration

 - [POST /integration](https://docs.transportial.com/apis/integration/create:integration.md): Creates an integration on your platform. The type property selects the concrete kind (manual, configured, fileTemplate, messageAutomation, apiAccess) and therefore which other properties apply.

Side effects on save:
- a schedule's interval is converted to milliseconds; a cron schedule that does not parse is rejected;
- every incoming web-hook data source without a token gets a random 15-character token, which forms its webhook URL;
- manual integrations get their sectorType from the connector and run the connector's own setup;
- configured integrations copy the first of triggerEvents into the legacy triggerEvent;
- file-template integrations get their matrix and HTML fingerprints computed.

Permission: create:integration

Errors: 400 when the body cannot be parsed (unknown type, wrong value types) or the schedule's cron expression is invalid; 400 with message integration_manual_type_unavailable when manualType is a connector that is not offered for new integrations (available: false in the platform's integrationManualTypes).

### Upload a file for a file template

 - [POST /integration/file/upload](https://docs.transportial.com/apis/integration/integration:fileupload.md): Uploads an example document for a file-template integration. The file is stored on your platform and converted to a cell matrix and an HTML rendering, which the template editor uses to place fields; the matching fingerprints are returned so the template can later recognise documents with the same layout.

Send multipart/form-data with the file in the part named file. PDF, Word and Excel (or other spreadsheet) content types are accepted.

Permission: integration:fileUpload

Errors: a response with success = false and code = 400 (HTTP status 200) when the file part is missing or its content type is not supported.

### Get virtual FTP connection details

 - [GET /integration/virtualFtp/details](https://docs.transportial.com/apis/integration/get:integration:virtualftpdetails.md): Returns the host and port of the platform-hosted (virtual) FTP server that virtual FTP data sources connect through. The values come from the running server's configuration, so they are correct for the current environment, unlike values stored on older data sources.

Permission: get:integration

### Test a file template mapping

 - [POST /integration/fileTemplate/mapping:test](https://docs.transportial.com/apis/integration/test:integration:filetemplatemapping.md): Applies an unsaved file-template integration to its own matrix (spreadsheet cells) and html (document rendering) and returns the JSON its fields produce, without saving anything.

When the first field is of type ARRAY the result is a JSON array (only object rows are kept); otherwise it is a JSON object.

Permission: create:integration

Errors: 400 when the body cannot be parsed.

### List tests of an integration

 - [GET /integration/{id}/test](https://docs.transportial.com/apis/integration/get:integration:tests.md): Returns the saved test cases of an integration: sample input, expected output and/or assertions, notification settings and the result of the last run.

At most the first 50 tests are returned; totalResults holds the total number of tests.

Permission: get:integration

Errors: 404 when no integration with this id exists on your platform.

### Create an integration test

 - [POST /integration/{id}/test](https://docs.transportial.com/apis/integration/create:integration:test.md): Saves a new test case for an integration: an input payload to run through one of its data sources, and the expected output and/or assertions the result is judged on. The test is not run on creation (lastStatus is NOT_RUN).

A dataSourceId that does not resolve is stored as no data source; running such a test fails with "No data source configured for this test".

Permission: create:integration

Errors: 400 when the body cannot be parsed; 404 when no integration with this id exists on your platform.

### Import data through a field mapping

 - [POST /integration/import/data](https://docs.transportial.com/apis/integration/import:integration:data.md): Maps a raw data payload onto platform entities with the given field mapping and creates those entities (or updates the existing entity the mapped data resolves to) on your platform. This is the backend of the import tool: upload a file, map its columns, import.

data may be JSON, XML, CSV, X12, EDIFACT, or a base64-encoded file (including Excel workbooks); the format is detected automatically. When the first mapping field is of type ARRAY the result is a list and one entity is created per mapped row; otherwise a single entity is created. Array fields addressed with an empty [] selector are appended to the entity the row resolves to instead of replacing its collection, so one row per child (for example one vehicle per row of a fleet) accumulates.

The created entities trigger their usual save side effects (live updates, message automations, integrations).

Permission: integration:import-data

Errors: 400 when the body cannot be parsed or entityType is unknown.

### Get an integration

 - [GET /integration/{id}](https://docs.transportial.com/apis/integration/get:integration.md): Returns one integration of your platform, serialised as its concrete type (selected by type), including its data sources, schedule and notes.

Permission: get:integration

Errors: 404 when no integration with this id exists on your platform, or it is deleted or archived.

### Delete an integration

 - [DELETE /integration/{id}](https://docs.transportial.com/apis/integration/delete:integration.md): Soft-deletes an integration: it is flagged as deleted, disappears from lists and lookups, and is no longer scheduled or triggered. Its logs, tasks and objects are kept.

Permission: delete:integration

Errors: 404 when no integration with this id exists on your platform, or it is already deleted or archived.

### Receive a webhook for an incoming data source

 - [GET /integration/{id}/dataSource/{dataSourceId}/{token}](https://docs.transportial.com/apis/integration/handle:integration:datasourcewebhook.md): Public endpoint an external system calls to push data into one incoming web-hook data source of an integration. Accepts POST, PUT, GET and DELETE. The full URL (/integration/{id}/dataSource/{dataSourceId}/{token}) is shown on the data source; token is generated when the data source is saved.

Authentication (decided by the data source; any mismatch, including a wrong dataSourceId/token pair, gives 401):
- hasAuthentication = true: the request must carry the data source's credentials, by authenticationMethod:
  - apiKey: header X-API-KEY equal to the credentials' apiKey;
  - accessKeys: headers X-API-KEY (access key) and X-API-SECRET (secret key);
  - bearer: Authorization: Bearer  (or an Authorization cookie) holding an unexpired JWT signed by Transportial;
  - basic: HTTP Basic authentication with the credentials' username and password;
  - any other method is always rejected.
- hasAuthentication = false: the caller's IP address must be in the data source's allowedIps.

Behaviour:
1. The raw body is always stored as an integration object first.
2. If the integration is disabled the object is marked ERROR and 405 is returned.
3. Otherwise a webhook task is created, authentication is checked and the payload is matched against the data source's filters (405 when it does not pass). A passing payload is mapped with the data source's field mapping and the resulting entities are created or updated; 201 is returned. Data sources with sequentialProcessing handle one call at a time.
4. If handling throws, the object is scheduled for retry, the error is logged and 500 is returned.

Permission: Public, no authentication (the data source's own authentication applies).

Errors: 401 when authentication fails or the data source/token pair is unknown; 404 when the integration does not exist; 405 when the integration is disabled or the filters reject the payload; 500 when handling failed.

### List the functions of a connector

 - [GET /integration/type/{type}/functions](https://docs.transportial.com/apis/integration/get:integration:functions.md): Returns the names of all functions (handlers) a manual connector offers, for example the scheduled syncs and event handlers of Transics, plus a description per function where one is available. The names are the values a manual integration's enabledFunctions accepts.

An unknown connector type returns an empty list rather than an error.

Permission: get:integrationFunctions

### Generate an integration with AI

 - [POST /integration/generate](https://docs.transportial.com/apis/integration/generate:integration.md): Uses your platform's configured AI model to draft an integration configuration from a free-text description and/or uploaded documents (for example API documentation). Nothing is saved: the draft is returned as integration JSON that the client can review and then send to POST /integration.

documents are file ids on your platform; ids that do not resolve to a file are ignored.

Permission: create:integration

Errors: 400 when the body cannot be parsed.

### Run all tests of an integration

 - [POST /integration/{id}/test/run](https://docs.transportial.com/apis/integration/run:integration:tests.md): Runs every saved test case of an integration, one after another, and returns each result plus pass/fail totals. Each test's last status is stored and failure notifications are sent as for a single run.

Permission: get:integration

Errors: 404 when no integration with this id exists on your platform.

### List HTTP request logs of an integration

 - [GET /integration/{id}/requestLogs/{offset}/{amount}](https://docs.transportial.com/apis/integration/get:integration:requestlogs.md): Returns one page of the outbound HTTP requests an integration made, newest first, with URL, request and response body and response code. Credentials are redacted before request logs are stored.

relations maps every returned request log id to its task (id and name), the task's integration object and sibling counts.

Filtering: all query parameters are optional and combined with AND; without any, the unfiltered list is returned. The same filter contract applies to the objects, tasks, logs and request-logs lists, so a filter can be kept while switching between them.

Pagination: offset and amount are path segments; totalResults holds the total number of matching rows.

Permission: get:integrationRequestLogs

Errors: 404 when no integration with this id exists on your platform.

### Receive an inbound email

 - [POST /integration/email](https://docs.transportial.com/apis/integration/handle:integration:email.md): Public endpoint called by SendGrid Inbound Parse for every email sent to an integration's inbound address (@...transportial.com). The email's form fields are stored as an integration object and handed to that data source.

Authentication:
- the recipient address selects the data source (by id) and thereby the integration and platform;
- when the platform has allowed email domains configured, the sender's domain must be one of them (400 otherwise);
- a data source with hasAuthentication = false only accepts calls from an IP address in its allowedIps (401 otherwise). SendGrid posts every inbound email to this one URL and cannot present per-data-source credentials, so for hasAuthentication = true the inbound address and the allowed sender domains are the gate.

Behaviour: when the integration is enabled and the email passes the data source's filters, it is mapped and handled like any incoming data source payload and 201 is returned; otherwise the object is marked ERROR and 405 is returned.

Permission: Public, no authentication (see above).

Errors: 400 when the form has no envelope or no recipient on a transportial.com domain, when the data source cannot be resolved ("Platform not found") or the sender's domain is not allowed; 401 when the caller's IP is not allowed; 405 when the integration is disabled or missing, or the filters reject the email.

### Generate a data source mapping with AI

 - [POST /integration/dataSource/mapping:generate](https://docs.transportial.com/apis/integration/generate:integrationdatasourcemapping.md): Uses your platform's configured AI model to generate the field mappings of one data source. The model receives the field structure of the target entity type, the direction and data type, and the optional description, sample data and documents. Nothing is saved: the returned fields can be assigned to the data source's fields directly.

documents are file ids on your platform; ids that do not resolve to a file are ignored.

Permission: create:integration

Errors: 400 when the body cannot be parsed or entityType is unknown.

### List tasks of an integration

 - [GET /integration/{id}/tasks/{offset}/{amount}](https://docs.transportial.com/apis/integration/get:integration:tasks.md): Returns one page of the integration tasks of an integration, newest first. A task is one run of a handler: a scheduled sync, a webhook call or the handling of one integration object.

relations maps every returned task id to its related counts and its integration object.

Filtering: all query parameters are optional and combined with AND; without any, the unfiltered list is returned. The same filter contract applies to the objects, tasks, logs and request-logs lists, so a filter can be kept while switching between them.

Pagination: offset and amount are path segments; totalResults holds the total number of matching rows.

Permission: get:integrationTasks

Errors: 404 when no integration with this id exists on your platform.

### List logs of an integration

 - [GET /integration/{id}/logs/{offset}/{amount}](https://docs.transportial.com/apis/integration/get:integration:logs.md): Returns one page of the log lines written by an integration, most recently updated first. Each log has a level (info, warning, success, error), a message and usually the task that wrote it.

relations maps every returned log id to its task (id and name), the task's integration object and sibling counts.

Filtering: all query parameters are optional and combined with AND; without any, the unfiltered list is returned. The same filter contract applies to the objects, tasks, logs and request-logs lists, so a filter can be kept while switching between them.

Pagination: offset and amount are path segments; totalResults holds the total number of matching rows.

Permission: get:integrationLogs

Errors: 404 when no integration with this id exists on your platform.

### Trace an integration activity row

 - [GET /integration/{id}/trace/{type}/{traceId}](https://docs.transportial.com/apis/integration/get:integration:trace.md): Reassembles the whole run around one activity row: the integration object that started it (if any), and every task of that run ordered oldest first, each with its own logs and request logs and its parent task for nesting.

A trace is capped at 200 tasks, 1000 logs and 1000 request logs; truncated is true when a cap was hit, and the paginated lists should be used for the rest.

Permission: get:integrationLogs

Errors: 400 when type is not one of object, task, log, requestLog; 404 when no integration with this id exists on your platform, or no row of the given type and id belongs to it.

### List objects queued for an integration

 - [GET /integration/{id}/objects/{offset}/{amount}](https://docs.transportial.com/apis/integration/get:integration:objects.md): Returns one page of the integration objects of an integration, newest first. An integration object is one queued unit of work: an entity change or an incoming payload (webhook, email, file) waiting for, being, or done being handled.

relations maps every returned object id to its related counts (tasks, logs and request logs produced while handling it).

The value search column contains the raw payload, so q can find the object that carried a given reference.

Filtering: all query parameters are optional and combined with AND; without any, the unfiltered list is returned. The same filter contract applies to the objects, tasks, logs and request-logs lists, so a filter can be kept while switching between them.

Pagination: offset and amount are path segments; totalResults holds the total number of matching rows.

Permission: get:integrationObjects

Errors: 404 when no integration with this id exists on your platform.

## FailedTasks

One list of the platform's failed and held-back work (feature `failedTasksView`): message automation runs that errored, integration objects that failed after their automatic retries, failed workflow runs, app webhooks that failed after all delivery attempts, Transportial network orders whose sync failed, and orders, consignments and trips on hold with events held back. Filter by kind, entity, integration, date and error text. Retrying hands each item to its own retry path (re-queue on the integration queue, a new workflow run, webhook redelivery, network resync, release of the hold) and records who did it; an item is retried once (in-place items again only after a cooldown), so a double click never sends twice. Dismissing hides an item, with who and when, until it fails again. What you see and may do follows the permissions of each kind.

### List failed and held-back items

 - [GET /failedTasks/{offset}/{amount}](https://docs.transportial.com/apis/failedtasks/get:failedtasks.md): Returns one page of the failed items of your platform, newest first, plus kinds: the kinds you may see with their counts for the current filters. Dismissed items and items whose retry replaced them are left out unless includeHandled is true.

Permission: per kind — get:messageAutomationTasks, get:integrationTasks, get:workflow, install:app (or an administrator role) for app webhooks, get:transportialNetwork and get:trip / get:consignment / get:transportOrder for held items. Kinds you lack are left out; feature failedTasksView.

Errors: 400 when the feature is off (failed_tasks_feature_disabled), offset is not an integer or a date is not ISO-8601; 401 when you may see no kind at all.

### Dismiss failed items

 - [POST /failedTasks/dismiss](https://docs.transportial.com/apis/failedtasks/dismiss:failedtasks.md): Marks each item as handled without retrying it, recording who and when. A dismissed item is hidden from the list until it fails again (list with includeHandled to see it). Dismissing twice is harmless. Results come per item.

Permission: the act permission of each kind (see the retry endpoint); feature failedTasksView.

Errors: 400 when the feature is off, the body is malformed or holds more than 200 items; 401 when you may see no kind at all.

### Retry failed items

 - [POST /failedTasks/retry](https://docs.transportial.com/apis/failedtasks/retry:failedtasks.md): Hands each item to its own retry path: a failed integration object or message automation run is put back on the integration queue (only from error, so a repeat is a no-op), a failed workflow run starts again as a new run after the request, an app webhook is redelivered as a new delivery, a Transportial network order is synced again, and a held order, consignment or trip is released (which sends the events held back). Each item is retried once; network orders again only after the platform's failedTaskRetryCooldownSeconds. Who retried and when is recorded. Results come per item; one refused item does not stop the others.

Permission: per kind — update:messageAutomation, update:integration, execute:workflow, install:app (or an administrator role), update:transportialNetworkOrder, release:trip / release:consignment / release:transportOrder; items you may not act on answer forbidden. Feature failedTasksView.

Errors: 400 when the feature is off, the body is malformed or holds more than 200 items; 401 when you may see no kind at all.

