Skip to content

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.

  • 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
  1. A weighing is completed in the scale system (the truck has been weighed and the ticket is closed).
  2. The scale system sends one POST request per weighing to Ditio. Ditio stores it and answers immediately with 200 OK.
  3. 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).
  4. 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.
  5. 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 projectNumber you 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.

MethodPOST
Path/api/external-weight-measure on the Mass Transport API ($DITIO_MASSTRANSPORT_BASE; production and test base URLs are in Environments)
AuthBearer token with the masstransportapiv1 scope (see Authentication)
BodyJSON, one weighing per request

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.

FieldTypeDescription
externalSystemIdstringA 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.
weightIdstringThe weighing ticket number as printed on the receipt. It becomes the trip’s receipt number in Ditio.
dateTimestring (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.
vehicleRegistrationNumberstringThe 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 cubicMeternumberThe 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.
projectNumberstringThe 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.
FieldTypeDescription
externalUuidstringYour system’s permanent unique id for the weighing (transaction id). If present, it is the duplicate key, which makes corrections and resends reliable.
departmentstringThe scale or site that made the weighing, e.g. a site code.
projectNamestringThe project or order name.
clientNumberstringThe customer number in your system.
clientNamestringThe customer name.
productNumberstringThe article or material number. Ditio users can filter weighings by task when this equals the task’s external id in Ditio.
productNamestringThe article or material name, e.g. "Pukk 0-32".
FieldTypeDescription
companystringThe company that operates the scale.
notesstringFree-text comment from the ticket.
unitPricenumberThe price per unit.
percentageDiscountnumberThe discount in percent.
priceExVatnumberThe total price excluding VAT.
priceIncVatnumberThe total price including VAT.
isCompletebooleanWhether the ticket is final in your system. Stored for reference only; Ditio doesn’t wait for it.
recordDeletedbooleanSend 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.

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:

cubicMeterInterpreted as
1One load. Ditio uses the truck’s registered capacity.
2One 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”.

{
"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
}

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.

StatusMeaningWhat to do
200Stored (created or updated), or cancelledNothing more to do
204A cancellation for a weighing Ditio doesn’t haveNothing more to do
400The body is not valid JSON, a field can’t be read as its type, or a number is nullFix the payload; don’t retry it unchanged
401Token missing, expired or without the masstransportapiv1 scopeFetch a new token and retry once, per Token Expiry
5xxTemporary error in DitioRetry with backoff. Retrying is safe (see below)

Sending a weighing is an upsert. Ditio looks for an existing weighing from your system with the same key:

  • With externalUuid, the key is externalSystemId + 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": true and Ditio removes the weighing.
  • Corrected time: a corrected dateTime must not be later than when the weighing was first sent.

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 DitioMatched byNotes
CompanyYour API credentialsA new weighing is stored for the Ditio customer whose credentials you used. You don’t send a company id.
VehiclevehicleRegistrationNumberCompared with the registration numbers of the customer’s vehicles in Ditio, including hired trucks shared with the customer.
TripVehicle + dateTimeThe 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 tripThe driver’s trip registrationThe 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 tripThe driver’s trip registrationMatching 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:

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

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.

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 each client_secret encrypted 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.

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.

Terminal window
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"
}'
import os
import 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 customer
token = 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-expiry
var (clientId, clientSecret) = LoadCustomerCredentials("customer-a"); // your secret store, one set per customer
var 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();
IssueCauseFix
401 Unauthorized that a token refresh doesn’t fixToken requested without the masstransportapiv1 scopeRequest the scope explicitly; see 401 — wrong scope
400 invalid_scope from the token endpointThe client isn’t granted masstransportapiv1The customer edits the client in Company Setup → Integration and adds the Ditio Masstransport scope
Requests fail (currently 500)externalSystemId is missingAlways send externalSystemId
Weighing stays External (2)The registration number doesn’t match a vehicle the customer has in DitioCheck 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 timeIt matches automatically once the driver registers the trip
Weighing matched to the wrong timedateTime sent as local time without an offset, or the scale clock is aheadSend UTC with a Z suffix or include the offset; keep the scale clock synchronised
Project leaders can’t see the weighingsprojectNumber doesn’t equal the Ditio project numberAgree project numbering with the customer, or map it before sending (see Project numbers)
Duplicate weighings after a correctionTicket corrected without externalUuid, with a new plate or departmentSend externalUuid so corrections update the original