Weighbridge Integration
When a truck crosses a weighbridge, the weighing ticket lives in the scale system, while the project’s mass transport lives in Ditio. The Weight Measure API connects the two. The scale system pushes every completed weighing to Ditio. Ditio matches it to the truck and the trip it belongs to, and the verified weight then follows the trip through approval and invoicing.
This page is the integration specification for scale-system vendors. It covers the flow, the payload, how matching works, and how authentication is set up.
When to use this
Section titled “When to use this”- You are a scale or weighbridge vendor building a standard Ditio connector
- You operate a weighbridge and want tickets to appear in Ditio automatically
- You want verified weights, instead of estimated loads, on the trips that drivers register in Ditio
How it works
Section titled “How it works”- A weighing is completed in the scale system (the truck has been weighed and the ticket is closed).
- The scale system sends one
POSTrequest per weighing to Ditio. Ditio stores it and answers immediately with200 OK. - Ditio matches the weighing in the background, normally within a minute. It uses the vehicle registration number and the weighing time to find the truck and the trip it was on (see Matching).
- The matched trip gets the verified weight. The trip’s quantity is replaced by the weighed tonnes (or m³), the ticket number is recorded as the trip’s receipt number, and the trip is marked as weight-verified.
- Ditio users review, approve and invoice the weighings and trips in
Ditio, as part of their normal mass-transport
workflow. Weighings are
listed per project using the
projectNumberyou send (see Project numbers).
The integration is push-only from the scale system’s point of view. You don’t need to poll Ditio, and you don’t need to know Ditio’s internal ids.
Endpoint
Section titled “Endpoint”| Method | POST |
| Path | /api/external-weight-measure on the Mass Transport API ($DITIO_MASSTRANSPORT_BASE; production and test base URLs are in Environments) |
| Auth | Bearer token with the masstransportapiv1 scope (see Authentication) |
| Body | JSON, one weighing per request |
The weighing payload
Section titled “The weighing payload”Field names are case-insensitive. Send ISO 8601 timestamps and plain JSON
numbers, and leave out fields you don’t have rather than sending null for a
number.
Send only completed weighings with a non-zero quantity. Ditio doesn’t hold back preliminary tickets: every weighing it receives is matched, and its quantity is written to the trip.
Required
Section titled “Required”| Field | Type | Description |
|---|---|---|
externalSystemId | string | A fixed id for your system, agreed with Ditio during onboarding (e.g. "acmescale"). It identifies the source of the data and is part of the duplicate check. A request without it fails. |
weightId | string | The weighing ticket number as printed on the receipt. It becomes the trip’s receipt number in Ditio. |
dateTime | string (date-time) | When the weighing was completed. Always include the time zone: UTC with a Z suffix (2026-10-02T07:41:12Z) or an explicit offset (2026-10-02T09:41:12+02:00). Use whole seconds (at most millisecond precision). It must never be later than the moment you send it; keep the scale’s clock synchronised. Ditio doesn’t reject a later time: it moves it back in whole hours, which can match the weighing to the wrong trip or to none. |
vehicleRegistrationNumber | string | The truck’s registration number. It is the key used to match the weighing to a vehicle. Send it in capitals without spaces, e.g. EL12345. |
ton or cubicMeter | number | The net quantity. Send ton in metric tonnes (not kg) for weighed loads. cubicMeter is only used when ton is 0. See Volume instead of weight. |
projectNumber | string | The customer’s project number in Ditio. Ditio users who don’t have administrator rights only see weighings whose projectNumber equals a Ditio project they have access to. Ditio accepts a weighing without it, but then only administrators see it. See Project numbers. |
Recommended
Section titled “Recommended”| Field | Type | Description |
|---|---|---|
externalUuid | string | Your system’s permanent unique id for the weighing (transaction id). If present, it is the duplicate key, which makes corrections and resends reliable. |
department | string | The scale or site that made the weighing, e.g. a site code. |
projectName | string | The project or order name. |
clientNumber | string | The customer number in your system. |
clientName | string | The customer name. |
productNumber | string | The article or material number. Ditio users can filter weighings by task when this equals the task’s external id in Ditio. |
productName | string | The article or material name, e.g. "Pukk 0-32". |
Optional
Section titled “Optional”| Field | Type | Description |
|---|---|---|
company | string | The company that operates the scale. |
notes | string | Free-text comment from the ticket. |
unitPrice | number | The price per unit. |
percentageDiscount | number | The discount in percent. |
priceExVat | number | The total price excluding VAT. |
priceIncVat | number | The total price including VAT. |
isComplete | boolean | Whether the ticket is final in your system. Stored for reference only; Ditio doesn’t wait for it. |
recordDeleted | boolean | Send true to cancel a weighing you have previously sent. See Corrections and cancellations. |
Ditio stores every field. Only the fields named in How a weighing is matched are used for matching. The other fields are shown in Ditio for review and export.
Volume instead of weight
Section titled “Volume instead of weight”When ton is 0 and cubicMeter is greater than 0, the weighing is treated
as a volume in m³. Two values have a special meaning:
cubicMeter | Interpreted as |
|---|---|
1 | One load. Ditio uses the truck’s registered capacity. |
2 | One load with a trailer. Ditio uses truck plus trailer capacity. |
For a weighbridge, always send ton. Don’t send cubicMeter: 1 or 2 unless
you mean “one load”.
Example
Section titled “Example”{ "externalSystemId": "acmescale", "externalUuid": "6f1c2a9e-4b7d-4a52-9d0e-1f2b3c4d5e6f", "weightId": "104233", "department": "QUARRY-01", "dateTime": "2026-10-02T07:41:12Z", "vehicleRegistrationNumber": "EL12345", "ton": 27.84, "cubicMeter": 0, "company": "Example Quarry AS", "clientNumber": "10442", "clientName": "Example Contractor AS", "projectNumber": "24017", "projectName": "Example Road Project", "productNumber": "3201", "productName": "Pukk 0-32", "notes": "Levert til rigg 2", "unitPrice": 189.5, "percentageDiscount": 0, "priceExVat": 5275.68, "priceIncVat": 6594.60, "isComplete": true, "recordDeleted": false}Response
Section titled “Response”On success, Ditio returns 200 OK with the stored weighing, including Ditio’s
own id. Your system only needs to check for a 2xx status code. Matching
happens after the response, so the status in the response of a new weighing
is always 0. The matching result is visible to users in Ditio.
Errors use standard HTTP status codes with a JSON body that describes the problem. Log the body; don’t parse it.
| Status | Meaning | What to do |
|---|---|---|
200 | Stored (created or updated), or cancelled | Nothing more to do |
204 | A cancellation for a weighing Ditio doesn’t have | Nothing more to do |
400 | The body is not valid JSON, a field can’t be read as its type, or a number is null | Fix the payload; don’t retry it unchanged |
401 | Token missing, expired or without the masstransportapiv1 scope | Fetch a new token and retry once, per Token Expiry |
5xx | Temporary error in Ditio | Retry with backoff. Retrying is safe (see below) |
Corrections and cancellations
Section titled “Corrections and cancellations”Sending a weighing is an upsert. Ditio looks for an existing weighing from your system with the same key:
- With
externalUuid, the key isexternalSystemId+externalUuid. - Without it, the key is
externalSystemId+weightId+department+vehicleRegistrationNumber.
If a match is found, the existing weighing is updated instead of creating a duplicate. This means:
- Retries are safe. Resending the same weighing never creates a duplicate.
- Corrections are a resend. If a ticket is corrected in your system (new weight, other truck, other time), send it again with the same key. Ditio replaces the weighing’s values and matches it again. A correction also resets the weighing’s verified, approved and invoiced marks in Ditio, so it is reviewed again.
- Cancellations: resend the weighing with
"recordDeleted": trueand Ditio removes the weighing. - Corrected time: a corrected
dateTimemust not be later than when the weighing was first sent.
How a weighing is matched
Section titled “How a weighing is matched”Matching runs automatically, normally within a minute of receiving the weighing. Weighings that don’t match yet are retried for up to a month. A weighing that arrives before the driver has registered the trip, or before the truck has been added in Ditio, is still picked up later.
| In Ditio | Matched by | Notes |
|---|---|---|
| Company | Your API credentials | A new weighing is stored for the Ditio customer whose credentials you used. You don’t send a company id. |
| Vehicle | vehicleRegistrationNumber | Compared with the registration numbers of the customer’s vehicles in Ditio, including hired trucks shared with the customer. |
| Trip | Vehicle + dateTime | The trip that vehicle was driving when the weighing happened: loaded between 24 hours before and 10 minutes after the weighing, and not unloaded more than 30 minutes before it. If several trips qualify, the closest in time wins. A trip that already has a different verified weighing is skipped. |
| Project, order and customer of the trip | The driver’s trip registration | The trip already belongs to a project and order (and through them a customer) in Ditio. Matching doesn’t change them. |
| Mass type / material of the trip | The driver’s trip registration | Matching doesn’t change it. productNumber and productName stay on the weighing for reference and export. |
The status on a stored weighing tells you how far matching got:
status | Meaning |
|---|---|
0 (Unmatched) | Not matched yet: either not processed yet, or the vehicle was found but no fitting trip. Matching keeps retrying. |
1 (Matched) | Connected to a trip, and the trip now carries the verified weight. |
2 (External) | No vehicle in Ditio with this registration number yet. Matching keeps retrying, so it matches once the truck is added in Ditio. |
A weighing that can’t be matched is never rejected. It stays visible in Ditio for review alongside the matched ones.
Project numbers
Section titled “Project numbers”projectNumber isn’t used to find the trip, but it decides where the
weighing is listed in Ditio. Ditio users who don’t have administrator rights
only see weighings whose projectNumber equals the number of a Ditio project
they have access to. Weighings with any other project number are only visible
to the customer’s administrators.
Before go-live, agree with the customer that the project (order) number in your system is the same as the project number in Ditio, or map it in your system before sending.
Authentication
Section titled “Authentication”Tokens, environments and caching work as described in Authentication. What is specific to weighbridges:
- Credentials are per Ditio customer. Each Ditio customer that receives weighings from you creates an API client for their company and gives you its credentials. Your system keeps one credential set per customer installation.
- One token per customer. If you serve several Ditio customers, pass each
customer’s own credentials when you request a token and cache one token per
customer (
client_id) — never reuse a token across customers. Store eachclient_secretencrypted and never log it. - Use the Mass Transport scope. The customer’s administrator
creates the client
with the Ditio Masstransport scope selected, and your system requests
tokens with
scope=masstransportapiv1.
Examples
Section titled “Examples”The examples get a token with the masstransportapiv1 scope (in shell, into
$TOKEN with DITIO_SCOPE=masstransportapiv1) as shown in
Getting a token in your scripts,
use the environment variables from
Environments, and default to
the test environment.
curl -X POST "$DITIO_MASSTRANSPORT_BASE/api/external-weight-measure" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "externalSystemId": "acmescale", "externalUuid": "6f1c2a9e-4b7d-4a52-9d0e-1f2b3c4d5e6f", "weightId": "104233", "department": "QUARRY-01", "dateTime": "2026-10-02T07:41:12Z", "vehicleRegistrationNumber": "EL12345", "ton": 27.84, "projectNumber": "24017", "productName": "Pukk 0-32" }'Python
Section titled “Python”import osimport requests# Paste get_token (/getting-started/authentication/#getting-a-token-in-your-scripts) above this.
MASSTRANSPORT = os.environ.get("DITIO_MASSTRANSPORT_BASE", "https://core-api.ditio.dev/masstransport")
def push_weighing(token: str, weighing: dict) -> dict: response = requests.post( f"{MASSTRANSPORT}/api/external-weight-measure", json=weighing, headers={"Authorization": f"Bearer {token}"}, timeout=30, ) response.raise_for_status() return response.json()
customer = load_customer_credentials("customer-a") # your secret store, one set per customertoken = get_token("masstransportapiv1", customer["client_id"], customer["client_secret"])stored = push_weighing(token, { "externalSystemId": "acmescale", "externalUuid": "6f1c2a9e-4b7d-4a52-9d0e-1f2b3c4d5e6f", "weightId": "104233", "department": "QUARRY-01", "dateTime": "2026-10-02T07:41:12Z", "vehicleRegistrationNumber": "EL12345", "ton": 27.84, "projectNumber": "24017", "productName": "Pukk 0-32",})print("Stored as", stored["id"])using System.Net.Http.Headers;using System.Net.Http.Json;
var massTransportBase = Environment.GetEnvironmentVariable("DITIO_MASSTRANSPORT_BASE") ?? "https://core-api.ditio.dev/masstransport";using var http = new HttpClient();
// DitioAuth.GetTokenAsync: see /getting-started/authentication/#token-expiryvar (clientId, clientSecret) = LoadCustomerCredentials("customer-a"); // your secret store, one set per customervar token = await DitioAuth.GetTokenAsync(http, "masstransportapiv1", clientId, clientSecret);http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var weighing = new{ externalSystemId = "acmescale", externalUuid = "6f1c2a9e-4b7d-4a52-9d0e-1f2b3c4d5e6f", weightId = "104233", department = "QUARRY-01", dateTime = new DateTime(2026, 10, 2, 7, 41, 12, DateTimeKind.Utc), vehicleRegistrationNumber = "EL12345", ton = 27.84, projectNumber = "24017", productName = "Pukk 0-32",};
var response = await http.PostAsJsonAsync($"{massTransportBase}/api/external-weight-measure", weighing);response.EnsureSuccessStatusCode();Common issues
Section titled “Common issues”| Issue | Cause | Fix |
|---|---|---|
401 Unauthorized that a token refresh doesn’t fix | Token requested without the masstransportapiv1 scope | Request the scope explicitly; see 401 — wrong scope |
400 invalid_scope from the token endpoint | The client isn’t granted masstransportapiv1 | The customer edits the client in Company Setup → Integration and adds the Ditio Masstransport scope |
Requests fail (currently 500) | externalSystemId is missing | Always send externalSystemId |
Weighing stays External (2) | The registration number doesn’t match a vehicle the customer has in Ditio | Check the plate on the ticket and send it without spaces; the customer adds or corrects the vehicle in Ditio and the weighing then matches automatically |
Weighing stays Unmatched (0) | No trip registered for that truck around the weighing time | It matches automatically once the driver registers the trip |
| Weighing matched to the wrong time | dateTime sent as local time without an offset, or the scale clock is ahead | Send UTC with a Z suffix or include the offset; keep the scale clock synchronised |
| Project leaders can’t see the weighings | projectNumber doesn’t equal the Ditio project number | Agree project numbering with the customer, or map it before sending (see Project numbers) |
| Duplicate weighings after a correction | Ticket corrected without externalUuid, with a new plate or department | Send externalUuid so corrections update the original |
Related
Section titled “Related”- External Weight Measures: endpoint reference, including the review endpoints used inside Ditio
- Authentication: credentials, scopes and environments
- Machine Integration: pushing excavator state data