The API lets your own system (warehouse, ERP, webshop) get rates, book shipments, download labels and follow tracking directly in fragtr. Prices are exactly the same as in the portal.
Do you have your own agreement with UPS, DHL, GLS, PostNord, Bring or DAO? Then you can connect it yourself under Backend and get your own rates in fragtr. We have written a simple step-by-step guide for each carrier.
fk_…). It is shown only once – store it like a password.Authorization: Bearer fk_…Base URL: https://fragtr.dk/api/v1 · All amounts are DKK excl. VAT · JSON in and out · Dates as YYYY-MM-DD.
curl https://fragtr.dk/api/v1/me \ -H "Authorization: Bearer fk_YOUR_KEY"
A customer's key always works on the customer's own account. An organization's key can work on the organization's customers (specify account_no) or as its own shipment ("account_no": "own").
| Method | Path | What | Access |
|---|---|---|---|
| GET | /me | Who the key belongs to | Read |
| GET | /accounts | Accounts the key can work on (?q= searches) | Read |
| POST | /quotes | Get rates – returns quote_id and offers (valid for 30 min.) | Read |
| POST | /bookings | Book an offer | Book |
| GET | /bookings | List – ?from=&to=&status=&q=&account_no=&limit= | Read |
| GET | /bookings/{nr} | A single booking (e.g. CL-2026-00012). ?refresh=1 fetches the latest status | Read |
| GET | /bookings/{nr}/label | The label as PDF | Read |
| POST | /bookings/{nr}/cancel | Cancel (only before pickup) | Book |
| GET | /stats | Key figures for a period – ?from=&to= | Read |
Booking status: booked, handling (handled manually), failed, error (can be retried), cancelled. The carrier's own status is in carrier_status.
POST /api/v1/quotes
{
"direction": "export", // export (from DK) · import (to DK) · cross (between two other countries)
"origin": {"country": "DK", "zip": "2600", "city": "Glostrup", "street": "Industrivej 4"},
"destination": {"country": "NO", "zip": "0162", "city": "Oslo", "street": "Karl Johans gate 12"},
"packages": [{"qty": 2, "kg": 4.5, "l": 40, "w": 30, "h": 30}],
"pickup_date": "2026-10-27", // optional – defaults to the next business day
"incoterm": "DAP" // optional
}
{
"quote_id": "4f1c…", "valid_minutes": 30,
"offers": [
{"offer_id": "ntg:42", "carrier": "UPS", "product_label": "UPS · Standard", "sell_dkk": 215.32,
"transit_hint": "Levering senest fre. kl. 23:30"}
]
}
POST /api/v1/bookings
{
"quote_id": "4f1c…",
"offer_id": "ntg:42",
"sender": {"name": "Nordlys Lager", "contact": "Pia", "street": "Industrivej 4", "phone": "+45 12 34 56 78",
"email": "lager@nordlys.dk"},
"receiver": {"name": "Bergen Interiør AS", "contact": "Ola", "street": "Karl Johans gate 12",
"phone": "+47 22 33 44 55", "email": "ola@bergen.no"},
"goods_description": "Bordlamper i messing", // required – what is in the package
"reference": "ORD-1001", // optional
"commodity_value": 4800 // only for shipments subject to customs (e.g. Norway, UK, USA)
}
Postal code, city and country are taken from the quote. A phone number is required for both sender and receiver. For import and cross trade, the sender's email must be filled in (the label is sent there). The response contains the booking with no, tracking_number and has_label.
curl -o label.pdf https://fragtr.dk/api/v1/bookings/CL-2026-00012/label -H "Authorization: Bearer fk_…"
Under Backend → Webhook you enter an https URL. Fragtr then sends a POST with JSON whenever something happens:
booking.created – a shipment has been bookedbooking.updated – new status or tracking from the carrierbooking.cancelled – the shipment has been cancelledPOST https://jeres-system.dk/fragtr/webhook
X-Fragtr-Event: booking.updated
X-Fragtr-Timestamp: 1792700000
X-Fragtr-Signature: sha256=5d1c…
{"event": "booking.updated", "sent_at": "2026-10-23T09:12:00+00:00",
"data": {"booking": {"no": "CL-2026-00012", "status": "booked", "tracking_number": "1Z…",
"carrier_status": {"code": "IN_TRANSIT", "label": "Undervejs", …}, …}}}
The signature is HMAC-SHA256 of "{timestamp}.{body}" using your signing secret (whsec_…, shown once). Reject messages where the signature does not match or the timestamp is more than 5 minutes old.
# Python
import hmac, hashlib, time
def ok(secret, ts, body, signature):
expected = "sha256=" + hmac.new(secret.encode(), f"{ts}.{body}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature) and abs(time.time() - int(ts)) < 300
// Node.js
const crypto = require("crypto");
function ok(secret, ts, body, signature) {
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(`${ts}.${body}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature)) && Math.abs(Date.now()/1000 - ts) < 300;
}
Respond with 2xx within 6 seconds. Use Send test in Backend to test the connection.
Errors are returned as {"error": "Explanation"} with an HTTP status: 400 invalid data, 401 missing/invalid key, 403 no access (e.g. a read-only key trying to book), 404 not found, 429 too many requests, 502 the carrier system did not respond – try again.
Send the header Accept-Language: en to get error messages in English (default is Danish).
At most 120 requests per minute per key. Delete a key under Backend if it has fallen into the wrong hands – it stops working immediately.
Clovr and organizations can connect Claude to fragtr and ask about revenue, margin, bookings and invoices – or let Claude get rates and book. Claude only sees what the user can see, and asks before changing anything.
https://fragtr.dk/mcp → Connect → log in with your fragtr login → Allow access.Claude Code and other MCP clients can also use an API key:
claude mcp add --transport http fragtr https://fragtr.dk/mcp \ --header "Authorization: Bearer fk_YOUR_KEY"