GateRev

API in webhooki

GateRev pošilja podpisana webhook sporočila vašemu sistemu ERP ali TMS, prevoznikom pa ponuja API za najavo obiska, dodelitev voznika in vozila ter sporočanje časa prihoda. Stran je zgrajena iz strojno berljivih shem, ki nastanejo iz kode, ki ta sporočila pošilja in sprejema.

Tehnična vsebina v nadaljevanju je v angleščini, tako kot sheme same: imena polj, kode in opisi so prevzeti iz njih dobesedno.

Webhooks

A site administrator registers an endpoint URL and the event types it receives in GateRev (an empty list means all types). Each signing secret (whsec_…) is shown once — when the endpoint is created and when the secret is rotated. Every delivery is an HTTP POST with a JSON body.

Envelope (schemaVersion 1.0)

FieldTypeRequiredNotes
schemaVersionconst "1.0"yes
idintegeryes> 0
typestring (102 values, listed below)yes
atstring (date-time)yes
dataany JSON valueyes

New fields may be added in a MINOR version; ignore fields you do not know. The shape of data depends on the event type.

Headers

HeaderValue
X-GateRev-EventEvent type, the same as type in the body.
X-GateRev-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256(secret, t + "." + raw body)>
X-GateRev-Signature-PreviousOnly while a secret is being rotated (24 h): the same signature made with the previous secret. Rotating again within that window replaces the previous secret — only the most recent previous secret is still used.

Verifying the signature

Compute the HMAC over the raw body exactly as received — do not re-serialise the parsed JSON. Reject signatures older than 300 s.

import { createHmac, timingSafeEqual } from "node:crypto";
// header = X-GateRev-Signature; rawBody = the request body exactly as received (string)
const [t, v1] = header.split(",").map(p => p.slice(p.indexOf("=") + 1));
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const ok = /^[0-9a-f]{64}$/.test(v1 ?? "")
  && timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
  && Math.abs(Date.now()/1000 - Number(t)) <= 300; // replay window

Delivery and retries

Event types (102)

Carrier API

One key per carrier and organisation, issued by the site administrator and shown once. The key only sees and announces visits at that organisation's sites.

Authentication: Authorization: Bearer cr_… (pattern ^Bearer\s+(cr_[0-9a-f]{32})$).

Limits

Counters are kept in the memory of each server process: a restart resets them. Treat the limits as an approximate upper bound, not a guarantee.

Errors

Errors are application/problem+json; branch on reasonCode, not on title.

FieldTypeRequiredNotes
titlestringyes
statusintegeryes≥ -9007199254740991
reasonCodestringyes
detailstringno

GET /api/carrier/visits

Visits of this carrier at the sites of the key's organisation, including visits it drives as a linked executor. Driver e-mail and phone are not returned.

Query parameters

FieldTypeRequiredNotes
limitintegerno≥ 1, ≤ 200; default 50; Maximum number of visits returned, newest first.
siteIdstringnolength 5–64; Only visits at this site (must belong to the key's organisation).

Response 200

FieldTypeRequiredNotes
carrierstringyesCarrier name bound to the key.
visitsarray of objectyes
visits[].visitIdstringyes
visits[].stateone of: DRAFT | SUBMITTED | REJECTED | APPROVED | AWAITING_ARRIVAL | ARRIVED | IN_YARD | AT_DOCK | AT_SCALE | WEIGHED | READY_TO_EXIT | DEPARTED | CLOSED | CANCELLED | NO_SHOW | BLOCKEDyes
visits[].reasonstringyes
visits[].roleone of: carrier | executoryescarrier = announced by this carrier; executor = driven for another carrier (driver and vehicle only).
visits[].platestring or nullyesnull until a vehicle is assigned.
visits[].trailerPlatestring or nullyes
visits[].createdAtstring (date-time)yes
visits[].etaAtstring (date-time) or nullyes
visits[].arrivedAtstring (date-time) or nullyes
visits[].departedAtstring (date-time) or nullyes
visits[].nalogobject or nullyesDispatch order (reference, quantity, unit); null for executors.
visits[].nalog.refstringyes
visits[].nalog.quantitynumber or nullyes
visits[].nalog.unitstring or nullyes
visits[].siteobjectyes
visits[].site.idstringyes
visits[].site.namestringyes
visits[].slotobject or nullyes
visits[].slot.startAtstring (date-time)yes
visits[].slot.endAtstring (date-time)yes
visits[].slot.dockstring or nullyes
visits[].dockstring or nullyesDock the vehicle actually goes to (an assigned dock wins over the booked one).
visits[].driverNamestring or nullyes
visits[].etaReasonstring or nullyes
visits[].driverUnavailableAtstring (date-time) or nullyes
visits[].driverUnavailableReasonstring or nullyes

Errors

StatusreasonCodeWhen
401carrier_key_requiredMissing, malformed, unknown or revoked key, or the carrier is blocked (one answer for all).
429too_many_requestsPer-key request limit exceeded.
400validation_failedQuery parameters out of range.

POST /api/carrier/visits

Announce a visit. The carrier is taken from the key, never from the body; driver and vehicle may be assigned later unless the site requires them at announcement.

Header Idempotency-Key (optional, ^[\x21-\x7e]{8,200}$): Same key and same body within 24 h return the first successful (201) response again, with replayed: true; same key with a different body returns 409. A rejected request (4xx, 429) does not hold the key — repeating it runs it again.

Request body (application/json)

FieldTypeRequiredNotes
driverNamestringnolength 2–200
driverPhonestringnolength 0–30
driverEmailstring (email)noGiven together with driverName; the driver receives the pass link.
docTypeone of: id_card | passport | driving_licencenoOptional driver document. The number is stored as a keyed hash plus its last 3 characters in readable form (for comparison at the gate). Given together with docNumber.
docNumberstringnolength 0–40
pickupCodestringnolength 4–40; Pickup code issued by the ordering party; requires dispatchRef and plate.
platestringnolength 3–20
trailerPlatestringnolength 0–20
containerNostringnolength 0–20; ISO 6346 container number (check digit is verified).
sealNumberstringnolength 0–60
reasonone of: loading | unloading | dropoff_trailer | pickup | otheryes
dispatchRefstringnolength 0–60
cargoDescstringnolength 0–500
quantitynumberno> 0, ≤ 1000000; Announced quantity; requires dispatchRef.
unitone of: kgno
etaAtstring (date-time)noExpected arrival. ISO 8601 in UTC: must end in Z — an offset such as +02:00 is rejected.
localeone of: en | sl | de | sr | hr | pl | ronodefault "en"; Language of the driver's pass and messages.
siteIdstringnolength 5–64; Required when the organisation has more than one site.
startAtstring (date-time)noRequested slot start; the outcome is reported in slotBooked. ISO 8601 in UTC: must end in Z — an offset such as +02:00 is rejected.

Response 201

FieldTypeRequiredNotes
visitIdstringyes
statusUrlstringnoDriver pass link — a credential; hand it to the driver only.
autoApprovedbooleanyes
slotBookedbooleannoPresent when startAt was sent: whether the slot was booked.
quantityAppliedconst falseno
replayedconst truenoPresent when the response is a replay for a repeated Idempotency-Key.

Errors

StatusreasonCodeWhen
401carrier_key_requiredMissing, malformed, unknown or revoked key, or the carrier is blocked (one answer for all).
429too_many_requestsPer-key request limit exceeded.
403csrf_origin_invalidAn Origin header is present and invalid (requests without Origin/Referer are not affected).
403csrf_origin_mismatchAn Origin header is present and foreign.
403csrf_referer_invalidA Referer header is present and invalid.
403csrf_referer_mismatchA Referer header is present and foreign.
413payload_too_largeBody larger than the limit.
400body_abortedBody not received completely.
400bad_jsonBody is not valid JSON.
400validation_failedBody does not match the schema; detail lists the fields.
409terms_not_acceptedThe carrier's administrator has not accepted the carrier portal terms.
409rules_not_acceptedThe key was issued under booking rules that are no longer current.
400idempotency_key_invalidIdempotency-Key does not match the pattern.
409idempotency_key_reusedIdempotency-Key already used with a different body.
409idempotency_in_progressA request with the same Idempotency-Key is still running — retry.
429carrier_quotaDaily announcement limit for this key exceeded.
404no_sitesiteId is not a site of the key's organisation.
400site_requiredThe organisation has several sites (or none) and siteId was not sent.
400assignment_requiredThe site requires plate and/or driver at announcement.
400doc_jmbg_nikadA national personal number was sent as a document number — never accepted.
400doc_kratakDocument number too short.
400doc_nepoznat_tipUnknown document type.
400doc_partialdocType and docNumber must be sent together.
400driver_partialdriverName and driverEmail must be sent together.
400quantity_requires_orderquantity sent without dispatchRef.
400invalid_platePlate cannot be normalised.
400invalid_trailer_plateTrailer plate cannot be normalised.
400invalid_containerContainer number is not ISO 6346.
400invalid_container_check_digitContainer number check digit is wrong.
400invalid_sealSeal number is not valid.
400pickup_code_needs_refpickupCode requires dispatchRef.
400pickup_code_needs_platepickupCode requires plate.
400pickup_code_not_expectedThe order does not use a pickup code.
400pickup_code_invalidPickup code does not match the order.
400pickup_code_lockedToo many wrong codes for this order — the code is checked at the gate.
403blacklistedRegistration rejected (the reason is not disclosed).
409duplicate_visitAn open visit already exists for this vehicle and driver that day at this site.

PATCH /api/carrier/visits/{id}

Assign or replace driver, vehicle or trailer on the carrier's own visit. Replacing the driver rotates the pass link; the old link stops working.

Request body (application/json)

FieldTypeRequiredNotes
driverNamestringnolength 2–200
driverEmailstring (email)nolength 0–200
driverPhonestringnolength 0–30
platestringnolength 3–20
trailerPlatestringnolength 0–20

Response 200

FieldTypeRequiredNotes
okconst trueyes
driverReplacedbooleanyes
vehicleChangedbooleanyes
linkRotatedbooleanyestrue when the driver changed: the old pass link stops working.
statusUrlstringnoNew pass link, present only when linkRotated is true.

Errors

StatusreasonCodeWhen
401carrier_key_requiredMissing, malformed, unknown or revoked key, or the carrier is blocked (one answer for all).
429too_many_requestsPer-key request limit exceeded.
403csrf_origin_invalidAn Origin header is present and invalid (requests without Origin/Referer are not affected).
403csrf_origin_mismatchAn Origin header is present and foreign.
403csrf_referer_invalidA Referer header is present and invalid.
403csrf_referer_mismatchA Referer header is present and foreign.
413payload_too_largeBody larger than the limit.
400body_abortedBody not received completely.
400bad_jsonBody is not valid JSON.
400validation_failedBody does not match the schema; detail lists the fields.
429assign_quotaDaily assignment limit for this key exceeded.
404not_foundNo such visit for this carrier and organisation.
409assignment_lockedThe vehicle has already entered or the visit is closed — the site changes it.
400driver_partialdriverName and driverEmail must be sent together.
400invalid_platePlate cannot be normalised.
400invalid_trailer_plateTrailer plate cannot be normalised.
400no_changeNothing to change.
409assignment_cutoffThe site's change deadline has passed — the site changes it.
409assignment_needs_reviewAfter manual approval the site changes driver and vehicle.
409pickup_code_boundThe vehicle is bound to the order's pickup code.
409pickup_code_driver_boundThe driver is bound to the order's pickup code.
403blacklistedAssignment rejected.
409watchlist_reviewThis change must be approved by the site.
409duplicate_visitAn open visit already exists for this vehicle and driver that day.
409conflictThe visit changed state meanwhile — read it again.

PUT /api/carrier/visits/{id}/eta

Report an absolute ETA from the carrier's TMS or telematics, optionally with a delay reason.

Request body (application/json)

FieldTypeRequiredNotes
etaAtstring (date-time)yesAbsolute arrival time. ISO 8601; a UTC offset such as +02:00 is allowed.
reasonone of: saobracaj | utovar | pauza | kvar | granicanoDelay reason: saobracaj = traffic, utovar = delay at loading, pauza = mandatory rest break (Regulation (EC) 561/2006), kvar = breakdown, granica = border crossing.

Response 200

FieldTypeRequiredNotes
okconst trueyes
etaAtstring (date-time)yes

Errors

StatusreasonCodeWhen
401carrier_key_requiredMissing, malformed, unknown or revoked key, or the carrier is blocked (one answer for all).
429too_many_requestsPer-key request limit exceeded.
403csrf_origin_invalidAn Origin header is present and invalid (requests without Origin/Referer are not affected).
403csrf_origin_mismatchAn Origin header is present and foreign.
403csrf_referer_invalidA Referer header is present and invalid.
403csrf_referer_mismatchA Referer header is present and foreign.
413payload_too_largeBody larger than the limit.
400body_abortedBody not received completely.
400bad_jsonBody is not valid JSON.
400validation_failedBody does not match the schema; detail lists the fields.
404not_foundNo such visit for this carrier and organisation.
409not_expectedThe visit is not waiting for arrival.
400eta_in_pastETA is in the past.
409eta_too_lateETA is beyond the allowed ceiling after the slot start (or after the first reported ETA).

Schema files

Both files are generated from the code. webhook-envelope.schema.json (urn:gaterev:webhook:envelope:1) is a JSON Schema draft 2020-12 document for the webhook body. carrier-api.schema.json (urn:gaterev:carrier-api:1) is not itself a schema: it is a collection that lists, per endpoint, the method, path, errors and a JSON Schema draft 2020-12 for the query, request body and response. Ask us for the current files.

Zadnja posodobitev: 2026-10-13