A step-by-step workflow for automating VGM capture, matching, submission and exception handling, with the code and logic you can build from today.

VGM is a hard cut-off. Miss it and the container doesn't load, full stop, no appeal to the terminal. Most forwarders handle it the way they've always handled it: someone emails a weighbridge ticket, someone else re-types the number into a carrier portal, and the whole chain depends on nobody being on leave that week. This article is the workflow I run to take the humans out of the repetitive parts and keep them only where judgement is actually needed: the exception queue.
The SOLAS VGM requirement (IMO, in force since 2016) gives you two legal ways to get the number: Method 1, weighing the packed container, or Method 2, weighing the cargo and adding the container's tare weight. Either way, the number has to reach the carrier before their cut-off, which is usually earlier than the physical gate cut-off.
The manual version of this process has three places it fails:
None of these are technology problems in the "we need AI" sense. They're data-plumbing problems. Fix the plumbing and the deadline stops being scary.
Treat every VGM input, whether it's a weighbridge ticket, a packing list, or a manual entry form, as a structured record the moment it enters your system. Don't let it sit as an attachment waiting for a human to read it.
{
"source_type": "weighbridge_ticket",
"container_no": "MSCU1234567",
"booking_no": "BKG998877",
"gross_weight_kg": 24850,
"method": "1",
"weighed_at": "2026-10-20T14:32:00Z",
"weighing_party": "ACME Weighbridge Ltd",
"raw_document_id": "doc_7f3a2b"
}
If the document is OCR'd rather than entered manually, flag the confidence score on the fields that matter (container number, weight) and route anything below your own threshold straight to a human reviewer instead of trusting it blind.
This is the step that fails most often in manual workflows, so make it deterministic. Match on container number first (it's the most reliable key, format-checked against ISO 6346), then cross-check against the booking number on file.
def match_vgm_record(record, bookings):
container = normalise_container_no(record["container_no"])
candidates = [b for b in bookings if b.container_no == container]
if not candidates:
return route_to_exception(record, reason="no_matching_booking")
if len(candidates) > 1:
return route_to_exception(record, reason="ambiguous_match")
booking = candidates[0]
if record.get("booking_no") and record["booking_no"] != booking.booking_no:
return route_to_exception(record, reason="booking_mismatch")
return attach_vgm(booking, record)
Run the ISO 6346 check digit validation on every container number before it touches this logic. A typo'd container number is the single most common reason a correct weight ends up on the wrong shipment. (Our container number validator runs exactly this check if you want to test a batch before wiring it into your own pipeline.)
Don't let a bad number reach the carrier. Basic sanity checks catch most of the obvious errors before they become a rejected submission:
def validate_vgm(record, container_spec):
errors = []
if record["gross_weight_kg"] <= container_spec.tare_weight_kg:
errors.append("gross_weight_below_tare")
if record["gross_weight_kg"] > container_spec.max_gross_kg:
errors.append("exceeds_container_max_payload")
if record["method"] not in ("1", "2"):
errors.append("invalid_method")
return errors
Anything that fails validation goes to the exception queue, not to the carrier. A rejected VGM submission at the carrier's end wastes more time than catching it yourself five minutes earlier.
Most carriers accept VGM via a portal, an EDI message (commonly carried over the same channels as other shipping instructions), or an API where one's published. Whichever channel you use, build the submission as a queued job with its own deadline, separate from the terminal's gate cut-off:
def schedule_submission(booking, vgm_record):
deadline = booking.carrier_vgm_cutoff
buffer = timedelta(hours=4)
submit_by = deadline - buffer
queue_submission(booking, vgm_record, run_at=min(now(), submit_by))
The buffer matters more than the automation itself. A bot that submits at the exact deadline is still fragile to a carrier portal outage. Give yourself room to retry.
Automating the happy path is the easy half. The queue that catches everything that didn't match, didn't validate, or didn't arrive in time is where the actual risk management lives. At minimum it needs:
| Exception type | Trigger | Action |
|---|---|---|
| No VGM received | Booking within N hours of cutoff, no weight on file | Alert ops, flag shipper |
| Ambiguous match | Multiple bookings share a container prefix pattern | Human review |
| Validation failure | Weight outside tare/max bounds | Return to source for correction |
| Submission failure | Carrier portal/EDI rejects or times out | Retry with escalating alert |
Set the "no VGM received" trigger well before the carrier's cutoff, not at it. If your cutoff is, say, 24 hours before vessel departure, alert at 48 hours so there's still a working day to chase the shipper.
Take your own numbers and run the arithmetic:
shipments per month handled manually = A
average minutes per VGM chase/entry/submit = B
minutes saved per shipment once automated = B - (automated handling minutes)
hours saved per month = A × (B - automated handling minutes) / 60
If a rolled container costs you real money in re-booking, demurrage, or client goodwill, the number that matters most isn't the time saved on the easy cases. It's how many fewer times per quarter a VGM miss causes a roll. Pull your own rolled-container log, count how many rolls over the last few quarters were caused by a missing or late VGM, and use that figure, together with your own re-booking and demurrage costs per incident, to work out what a tighter exception queue would be worth to you.
If your bookings and container data already live in CargoWise, the matching and validation logic above maps cleanly onto shipment and container records you're already maintaining. The eAdapter series covers how to wire CargoWise data into exactly this kind of automated pipeline, including the event hooks you'd use to trigger a VGM capture the moment a container number is confirmed.
Run a batch of your current container numbers through the container number validator to see how many would have failed the ISO 6346 check before ever reaching a carrier portal, and go from there.
Join freight forwarders saving hours every week with CargoMode.
Start for free