API-dokumentaatio

Rajapinnalla tilaaja hoitaa tilauksen koko elinkaaren omasta toiminnanohjauksestaan: luo tilauksen, valitsee tarjouksen tai määrää tutun auton, seuraa ajoa saapumisineen, kuittauksineen ja kuvineen, muuttaa reittiä ajon aikana, arvioi kuljetusliikkeen, hoitaa reklamaatiot ja saa ilmoituksen omaan osoitteeseensa, kun jotain muuttuu. Vastaukset ja virhekoodit ovat englanniksi, jotta ohjelma voi haarautua niiden mukaan.

Koneluettava kuvaus (OpenAPI 3.1) Postmaniin, Insomniaan tai asiakasgeneraattoriin: https://www.rahtis.eu/api/v1/openapi.json

Avaimet ja webhookit luodaan kabinetissa, välilehdellä API.

Aloitus

Luo avain kabinetissa. Avain näytetään vain kerran — tallenna se salaisuuksien hallintaan. Lukuavain riittää tilausten seurantaan; kaikkiin muutoksiin (luonti, valinta, määrääminen, reittimuutokset, arvio, reklamaatiot) tarvitaan kirjoitusoikeus.

Testiyrityksen avain alkaa rhs_test_ ja näkee vain testiympäristön tilaukset; vastauksissa on silloin otsake Rahtis-Environment: test. Oikean yrityksen avain alkaa rhs_live_.

Avain annetaan jokaisessa pyynnössä otsakkeessa Authorization: Bearer.

curl https://www.rahtis.eu/api/v1/orders?status=OPEN,IN_PROGRESS \
  -H "Authorization: Bearer $RAHTIS_KEY"

Pääsy, ajat ja rahat

Rajapintaa käyttää hyväksytty tilaajayritys: hae mukaan sivulla rahtis.eu/fi/apply, ja kun yritys on hyväksytty ja tiedot täytetty, avaimet luodaan kabinetin API-välilehdellä. Rajapinnan käyttö on maksutonta.

Ajat: rajapinnan aikaleimat (created_at, arrival.at, eta.at …) ovat UTC-aikaa ISO 8601 -muodossa. Reittipisteen scheduled_date ja scheduled_time ovat paikallista aikaa pisteen maassa (Suomi, Ruotsi, Norja, Tanska) — sama, jonka kuljettaja näkee.

Rahat: kaikki summat ovat euroina ilman arvonlisäveroa (vat_included: false). Arvonlisävero lisätään laskulla maan mukaan.

Tilauksen tilat

Tilaus kulkee tilasta toiseen alla olevassa järjestyksessä. Jokaisesta muutoksesta tulee webhook (order.taken, order.started, order.reopened, order.closed, order.cancelled), ja muutoksen näkee myös GET /orders/{ref}/events.

deadline_at on päätöksen määräaika: tilaajalla on 15 minuuttia valita tarjous ensimmäisestä tarjouksesta, ja valitulla kuljetusliikkeellä 15 minuuttia vahvistaa. Jos määräaika umpeutuu, tilaus palaa tilaan OPEN. Suoralla määräyksellä (POST /orders/{ref}/assign) määräaikaa ei ole: tilaus odottaa auton vahvistusta.

Tila
OPENtarjouspöydällä, odottaa tarjouksia (enintään kolme)
REQUESTEDtarjouksia on tullut — valitse yksi (POST …/offers/{id}/choose) deadline_at mennessä
AWAIT_DRIVERauto on valittu tai määrätty, odottaa kuljetusliikkeen tai kuljettajan vahvistusta
IN_PROGRESSvahvistettu, ajo käynnissä: reittipisteitä kuitataan
DONEajo päättynyt: maksut ja odotusaikalisä on kirjattu, asiakirjat saatavilla
CANCELLEDperuttu (POST …/withdraw tai kabinetista)
DRAFTluonnos kabinetissa, ei julkaistu — rajapinnan kautta luotu tilaus julkaistaan heti

Tilaustyypit ja reittipisteet

haul_kind kertoo, mitä kuljetetaan: TRAILER (puoliperävaunu), CONTAINER (kontti; container_feet pakollinen) — näissä kuljetusyksikkö on numeroitu, ja trailer_plate (perävaunun rekisterinumero tai ISO 6346 -konttinumero) on pakollinen. VAN ja TRUCK ovat pikakuljetuksia autossa: ldm (lastausmetrit) ja vähintään yhden pisteen cargo_weight_kg ovat pakollisia.

Pisteet annetaan ajojärjestyksessä (2–20). Jokaisessa tilauksessa on yksi PICKUP. Alla, mitä kukin tyyppi vaatii; puuttuvasta pisteestä tulee 422 ja selitys kentässä details.

Lähetä kontrakti kentässä contract: RAHTIS (oletus) — RAHTIS-palvelu, palvelumaksu 5 % hinnasta, vähintään 15 € keikalta yhdessä kuljetusliikkeen maksun kanssa; DIRECT — suora sopimus kuukausimaksua käyttävän kuljetusliikkeen kanssa, ei palvelumaksua, ja tilauksen näkevät vain tällaiset kuljetusliikkeet. Kenttä contract näkyy myös tilauksen tiedoissa.

Tyyppi
TRAILER_SWAP (TRAILER, CONTAINER)Perävaunun tai kontin vaihto: PICKUP (yksikön nouto) → vähintään yksi DELIVERY, EXTRA_LOAD tai EXTRA_UNLOAD → TRAILER_RETURN (mihin yksikkö jätetään). Siirtoajo on PICKUP → TRAILER_RETURN ilman välipisteitä: yksikkö vain siirretään, ja keikka päätetään luovutuskuvalla rahtikirjan sijasta.
ONE_WAY (TRAILER, CONTAINER)Yksikkö noudetaan ja viedään perille: PICKUP → vähintään yksi DELIVERY, EXTRA_LOAD tai EXTRA_UNLOAD; TRAILER_RETURN ei pakollinen.
ROUND_TRIP (TRAILER, CONTAINER)Kuten ONE_WAY, mutta yksikkö palaa lähtöpaikkaan — lisää loppuun TRAILER_RETURN.
ONE_WAY (VAN, TRUCK)Pikakuljetus: PICKUP (lastaus) → DELIVERY (purku), välissä EXTRA_LOAD / EXTRA_UNLOAD tarvittaessa. order_type on aina ONE_WAY.
Rooli
PICKUPnouto: yksikön nouto tai tavaran lastaus — aina ensimmäinen
DELIVERYpurku vastaanottajalle; company_name pakollinen
EXTRA_LOADlisälastaus matkalla; consignee kertoo, kenelle lisäkuorma on
EXTRA_UNLOADlisäpurku matkalla
TRAILER_RETURNtyhjän tai lastatun yksikön jättöpaikka (trailer_loaded kertoo kumpi)

Tilausten synkronointi

GET /orders palauttaa yrityksen tilaukset muutosajan mukaan vanhimmasta uusimpaan. Tallenna viimeksi käsitellyn tilauksen updated_at ja anna se seuraavalla kerralla parametrina updated_since. Jos vastauksessa on next_cursor, hae seuraava sivu parametrilla cursor, kunnes next_cursor on null.

Suodattimet: status (pilkuin eroteltu luettelo, esim. OPEN,IN_PROGRESS) ja limit (1–100, oletus 50).

let cursor = null;
do {
  const url = new URL('https://www.rahtis.eu/api/v1/orders');
  url.searchParams.set('updated_since', lastSync);
  if (cursor) url.searchParams.set('cursor', cursor);
  const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.RAHTIS_KEY}` } });
  const page = await res.json();
  for (const order of page.data) await save(order);   // lastSync = order.updated_at
  cursor = page.next_cursor;
} while (cursor);

Tilaus, tapahtumat ja asiakirjat

GET /orders/{ref} palauttaa tilauksen reittipisteineen, etenemisen ja kuljettajan ajoneuvon, kun kuljetusliike on ottanut tilauksen.

GET /orders/{ref}/events on aikajana: tilan muutokset, saapumiset ja kuittaukset reittipisteillä, asiakirjat ja reittimuutokset.

GET /orders/{ref}/documents palauttaa CMR:t ja rahtikuvat. Jokainen linkki on voimassa 5 minuuttia — hae luettelo uudelleen, kun tarvitset tiedoston.

Tilauksen luonti

POST /orders julkaisee tilauksen pöydälle samoin säännöin kuin lomake. Jokaisella reittipisteellä on oltava tarkka sijainti: lähetä location { lat, lon } tai osoite talonumeroineen, jonka paikannus tunnistaa täsmälleen annetussa kaupungissa. Muuten vastaus on 422 ja pyyntö lähettää koordinaatit — emme arvaa naapurikaupunkia.

Matka lasketaan kuorma-auton reittinä. Hinta annetaan euroina ilman arvonlisäveroa.

Lähetä otsake Idempotency-Key (esim. oma tilausnumerosi tai UUID). Jos yhteys katkeaa ja toistat pyynnön samalla avaimella, saat ensimmäisen tilauksen etkä luo toista. Avain muistetaan vuorokauden.

curl -X POST https://www.rahtis.eu/api/v1/orders \
  -H "Authorization: Bearer $RAHTIS_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: PO-4471" \
  -d '{
    "order_type": "ONE_WAY",
    "shipper_ref": "PO-4471",
    "rate": { "amount": 300 },
    "trailer": "Curtainsider 13.6 m",
    "trailer_plate": "ABC-123",
    "stops": [
      { "role": "PICKUP", "address": "Satamatie 1", "city": "Hanko", "country": "FI",
        "location": { "lat": 59.8208, "lon": 22.9565 },
        "scheduled_date": "2026-10-05", "cargo_weight_kg": 20000 },
      { "role": "DELIVERY", "address": "Tikkurilantie 10", "city": "Vantaa", "country": "FI",
        "company_name": "Vastaanottaja Oy", "scheduled_date": "2026-10-05" }
    ]
  }'
HTTP/1.1 422
{
  "error": {
    "code": "unprocessable",
    "message": "Some stops are invalid.",
    "details": [
      { "field": "stops[1].address",
        "issue": "address could not be located precisely; send location { lat, lon }" }
    ]
  }
}

Tilauksen peruminen

POST /orders/{ref}/withdraw peruu tilauksen; runko { "reason": "…" } on vapaaehtoinen. Suoritettua ajoa ei voi perua (409).

curl -X POST https://www.rahtis.eu/api/v1/orders/RS-2026-0142/withdraw \
  -H "Authorization: Bearer $RAHTIS_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Customer cancelled" }'

Ajon seuranta

Jokaisella reittipisteellä on eta (arvioitu saapuminen, päivittyy liikenteen mukaan jokaisen kuittauksen jälkeen), arrival (kuljettaja saapui) ja completion (piste kuitattu). Saapumisessa ja kuittauksessa on aika ja position: laitteen sijainti sillä hetkellä sekä distance_m, etäisyys pisteen osoitteesta. Etäisyydestä näkee, oliko kuljettaja paikalla — esimerkiksi odotusajan selvittämiseksi.

GET /orders/{ref}/documents palauttaa jokaiselle kuvalle ja CMR:lle captured_position (kuvauspaikka ja etäisyys pisteestä) sekä damage, jos kyse on vauriokuvasta.

Sijainti on yksi piste kyseiseltä hetkeltä, ei jatkuvaa seurantaa. Pisteet säilytetään 24 kuukautta ajon päättymisestä.

GET https://www.rahtis.eu/api/v1/orders/RS-2026-0142

"stops": [{
  "sequence": 1, "role": "DELIVERY", "city": "Vantaa",
  "eta": { "at": "2026-10-05T12:40:00Z", "source": "TRAFFIC", "updated_at": "2026-10-05T10:02:11Z" },
  "arrival": { "at": "2026-10-05T12:31:08Z",
               "position": { "lat": 60.2936, "lon": 25.0381, "accuracy_m": null, "distance_m": 24 } },
  "completion": { "at": "2026-10-05T13:05:40Z",
                  "position": { "lat": 60.2935, "lon": 25.0379, "accuracy_m": 12, "distance_m": 18 } }
}]

Tarjoukset ja auton määrääminen

GET /orders/{ref}/offers näyttää kuljetusliikkeiden tarjoamat autot kuljettajineen ja arvioineen. POST /orders/{ref}/offers/{id}/choose valitsee tarjouksen.

Tuttu auto määrätään suoraan: GET /vehicles antaa autot, jotka ovat ajaneet teille, ja POST /orders/{ref}/assign { "vehicle_id" } määrää sen tilaukselle, jolla ei vielä ole tarjouksia. Kuljetusliike ja kuljettaja saavat ilmoituksen.

POST /orders/{ref}/unassign peruu määräyksen ennen ajon alkua, ja tilaus palaa pöydälle.

curl https://www.rahtis.eu/api/v1/vehicles -H "Authorization: Bearer $RAHTIS_KEY"

curl -X POST https://www.rahtis.eu/api/v1/orders/RS-2026-0142/assign \
  -H "Authorization: Bearer $RAHTIS_KEY" -H "Content-Type: application/json" \
  -d '{ "vehicle_id": "9451acc9-5893-4741-b2c1-21f1ecbf0113" }'

Muutokset ajon aikana ja arvio

Ajon aikana reittipistettä voi muuttaa: PATCH /orders/{ref}/stops/{sequence} — vain lähetetyt kentät muuttuvat, null tyhjentää kentän. POST /orders/{ref}/stops lisää lisälastauksen tai -purun (before_sequence kertoo paikan), DELETE /orders/{ref}/stops/{sequence} poistaa ohittamattoman pisteen. Kuormausta ja perävaunun palautusta ei poisteta. Kuljetusliike saa ilmoituksen muutoksesta, ja reitti lasketaan uudelleen.

POST /orders/{ref}/reprice { "rate": { "amount" } } muuttaa hinnan; matka on oletuksena uudelleen laskettu reitti. GET /orders/{ref}/amendments listaa muutokset ja sen, onko kuljetusliike kuitannut ne.

Ajon päätyttyä POST /orders/{ref}/rating { "score": 1–5, "comment" } arvioi kuljetusliikkeen; uusi arvio korvaa edellisen.

curl -X PATCH https://www.rahtis.eu/api/v1/orders/RS-2026-0142/stops/1 \
  -H "Authorization: Bearer $RAHTIS_KEY" -H "Content-Type: application/json" \
  -d '{ "scheduled_time": "14:30", "note": "Gate 3", "contact": { "name": "Anna", "phone": "+358401234567" } }'

curl -X POST https://www.rahtis.eu/api/v1/orders/RS-2026-0142/reprice \
  -H "Authorization: Bearer $RAHTIS_KEY" -H "Content-Type: application/json" \
  -d '{ "rate": { "amount": 360 } }'

Reklamaatiot

GET /claims listaa yrityksen jättämät ja sitä koskevat reklamaatiot, GET /claims/{ref} näyttää viestit ja liitteet (linkit voimassa 5 minuuttia).

POST /claims { "order_ref", "kind", "description", "stop_sequence"?, "amount"? } jättää reklamaation käynnissä olevasta tai päättyneestä ajosta. POST /claims/{ref}/comments lisää viestin ja POST /claims/{ref}/attachments liitteen (multipart/form-data: file ja note; PDF, JPEG, PNG tai WebP, enintään 10 Mt). Toinen osapuoli ja ylläpitäjä saavat ilmoituksen sähköpostilla, kuten kabinetista jätettäessä.

curl -X POST https://www.rahtis.eu/api/v1/claims \
  -H "Authorization: Bearer $RAHTIS_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: claim-PO-4471" \
  -d '{ "order_ref": "RS-2026-0142", "kind": "DOWNTIME", "stop_sequence": 1,
        "description": "Waited 2 h at the unloading gate.", "amount": { "amount": 120 } }'

curl -X POST https://www.rahtis.eu/api/v1/claims/CL-RS-2026-0142-1/attachments \
  -H "Authorization: Bearer $RAHTIS_KEY" \
  -F "file=@waiting-slip.pdf;type=application/pdf" -F "note=Gate log"
kind
CARGO_DAMAGElastin tai perävaunun vaurio
SHORTAGEvajaus
DOWNTIMEodotusaika
DEVIATIONpoikkeama reitistä tai aikataulusta
OTHERmuu

Webhookit

Kabinetissa annetaan https-osoite ja valitaan tapahtumat. Allekirjoitussalaisuus näytetään kerran. Osoitteen on oltava julkinen (portti 443); uudelleenohjauksia ei seurata.

Ilmoituksessa on tilausnumero ja se, mikä muuttui — koko tilaus haetaan tarvittaessa GET /orders/{ref}.

Vastaa 2xx 10 sekunnin kuluessa ja käsittele ilmoitus vasta sen jälkeen. Muuten toimitus yritetään uudelleen 1, 5, 15 ja 60 minuutin sekä 3, 6, 12 ja 24 tunnin kuluttua. Sama ilmoitus voi tulla kahdesti: tunnista se kentästä id. Ilmoitukset lähetetään rinnakkain eivätkä välttämättä saavu syntymisjärjestyksessä — järjestä ne kentän created_at mukaan. Kabinetin painike ”Lähetä testi” lähettää tapahtuman ping.

TapahtumaMilloin
offer.receivedkuljetusliike tarjosi autoa tai auto määrättiin suoraan
order.takenkuljetusliike otti tilauksen
order.startedkuljettaja vahvisti, keikka alkoi
order.reopenedtilaus palasi pöydälle
order.stop_arrivedkuljettaja saapui reittipisteeseen
order.stop_completedreittipiste ohitettu
order.eta_changedarvioitu saapumisaika muuttui vähintään 5 min
order.lateauto myöhästyy pisteestä yli 30 min
order.waitingmaksuton odotustunti pisteessä päättyi
order.amendedreittiä tai hintaa muutettiin
order.closedkeikka päättyi
order.cancelledtilaus peruttu
document.addedrahtikirja tai kuva lisätty
claim.updatedreklamaatio jätetty, viesti, liite tai tila muuttui
POST /your/webhook
Rahtis-Event: order.stop_completed
Rahtis-Delivery: 3f1c9a52-6a0e-4c43-9d7e-5b2f0e8d1a44
Rahtis-Signature: t=1790000000,v1=5d1e…

{
  "id": "3f1c9a52-6a0e-4c43-9d7e-5b2f0e8d1a44",
  "type": "order.stop_completed",
  "created_at": "2026-10-05T09:12:44.120Z",
  "data": {
    "ref": "RS-2026-0142",
    "shipper_ref": "PO-4471",
    "stop": { "sequence": 0, "role": "PICKUP", "city": "Hanko",
              "completed_at": "2026-10-05T09:12:43.980Z" }
  }
}

Allekirjoituksen tarkistus

Otsake Rahtis-Signature: t=<unix-aika>,v1=<heksa>. v1 on HMAC-SHA256 salaisuudella merkkijonosta "<t>.<runko sellaisenaan>". Laske se raakarungosta ennen JSON-jäsennystä, vertaa vakioaikaisesti ja hylkää, jos t on yli 5 minuuttia vanha.

Tarkistusesimerkki: salaisuus whsec_test, t=1700000000, runko {} → v1 = 35495024f4ef3f94e5a93e22221544c4b75e9a42300cd965ab81cb85cd994e91.

import crypto from 'node:crypto';

// rawBody: the request body exactly as received (Buffer or string), before JSON.parse
function verify(header, rawBody, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && parts.v1?.length === 64 &&
    crypto.timingSafeEqual(Buffer.from(parts.v1, 'hex'), Buffer.from(expected, 'hex'));
}
import hmac, hashlib, time

def verify(header: str, raw_body: bytes, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    signed = parts["t"].encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    fresh = abs(time.time() - int(parts["t"])) < 300
    return fresh and hmac.compare_digest(parts.get("v1", ""), expected)

Virheet

Virheen runko on { "error": { "code", "message", "details"? } }. details kertoo kentän ja syyn, kun vika on pyynnössä.

KoodiMerkitys
bad_requestpyyntö on väärin muotoiltu (400)
unauthorizedavain puuttuu, on väärä tai peruttu (401)
forbiddenlukuavain, yritys ei ole aktiivinen tai voimassa olevia ehtoja ei ole hyväksytty (403)
not_foundtilausta, pistettä, tarjousta tai reklamaatiota ei ole yrityksellä (404)
conflicttila ei salli toimintoa tai sama Idempotency-Key on käsittelyssä (409)
unprocessablesisältö ei kelpaa: sijainti, reitti tai liiketoimintasääntö (422)
rate_limitedyli 60 pyyntöä minuutissa (429, Retry-After)
internalodottamaton virhe, pyyntöä ei suoritettu (500)

Rajat ja lokit

60 pyyntöä minuutissa avainta kohden. Pyynnöistä kirjataan avain, menetelmä, polku, vastauskoodi ja kesto; loki säilytetään 90 päivää. Webhook-toimitusten loki säilytetään 30 päivää, ja 100 peräkkäisen epäonnistumisen jälkeen osoite poistetaan käytöstä.

Avain lakkaa toimimasta, kun se perutaan tai kun yrityksen tai avaimen luoneen käyttäjän oikeudet päättyvät.

Muutokset rajapintaan

Versio on polussa (/api/v1) ja kuvauksessa (info.version). Uusia kenttiä, tapahtumia ja arvoja voi tulla — ohita tuntemattomat kentät ja arvot.

Aivomaa Oy · 3592993-6 · Suomi