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 | |
|---|---|
| OPEN | tarjouspöydällä, odottaa tarjouksia (enintään kolme) |
| REQUESTED | tarjouksia on tullut — valitse yksi (POST …/offers/{id}/choose) deadline_at mennessä |
| AWAIT_DRIVER | auto on valittu tai määrätty, odottaa kuljetusliikkeen tai kuljettajan vahvistusta |
| IN_PROGRESS | vahvistettu, ajo käynnissä: reittipisteitä kuitataan |
| DONE | ajo päättynyt: maksut ja odotusaikalisä on kirjattu, asiakirjat saatavilla |
| CANCELLED | peruttu (POST …/withdraw tai kabinetista) |
| DRAFT | luonnos 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 | |
|---|---|
| PICKUP | nouto: yksikön nouto tai tavaran lastaus — aina ensimmäinen |
| DELIVERY | purku vastaanottajalle; company_name pakollinen |
| EXTRA_LOAD | lisälastaus matkalla; consignee kertoo, kenelle lisäkuorma on |
| EXTRA_UNLOAD | lisäpurku matkalla |
| TRAILER_RETURN | tyhjä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_DAMAGE | lastin tai perävaunun vaurio |
| SHORTAGE | vajaus |
| DOWNTIME | odotusaika |
| DEVIATION | poikkeama reitistä tai aikataulusta |
| OTHER | muu |
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.
| Tapahtuma | Milloin |
|---|---|
| offer.received | kuljetusliike tarjosi autoa tai auto määrättiin suoraan |
| order.taken | kuljetusliike otti tilauksen |
| order.started | kuljettaja vahvisti, keikka alkoi |
| order.reopened | tilaus palasi pöydälle |
| order.stop_arrived | kuljettaja saapui reittipisteeseen |
| order.stop_completed | reittipiste ohitettu |
| order.eta_changed | arvioitu saapumisaika muuttui vähintään 5 min |
| order.late | auto myöhästyy pisteestä yli 30 min |
| order.waiting | maksuton odotustunti pisteessä päättyi |
| order.amended | reittiä tai hintaa muutettiin |
| order.closed | keikka päättyi |
| order.cancelled | tilaus peruttu |
| document.added | rahtikirja tai kuva lisätty |
| claim.updated | reklamaatio 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ä.
| Koodi | Merkitys |
|---|---|
| bad_request | pyyntö on väärin muotoiltu (400) |
| unauthorized | avain puuttuu, on väärä tai peruttu (401) |
| forbidden | lukuavain, yritys ei ole aktiivinen tai voimassa olevia ehtoja ei ole hyväksytty (403) |
| not_found | tilausta, pistettä, tarjousta tai reklamaatiota ei ole yrityksellä (404) |
| conflict | tila ei salli toimintoa tai sama Idempotency-Key on käsittelyssä (409) |
| unprocessable | sisältö ei kelpaa: sijainti, reitti tai liiketoimintasääntö (422) |
| rate_limited | yli 60 pyyntöä minuutissa (429, Retry-After) |
| internal | odottamaton 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