{"openapi":"3.1.0","info":{"title":"iBOX Shipping API","version":"1.0.0","description":"Book parcel-locker deliveries in Kosovo and let your customers track them.\n\n**Authentication.** Send your key as `Authorization: Bearer ibx_live_...`. Keys are\nissued per organization and scope every request to it; the organization is never\ntaken from a request body. A key is shown once at creation and stored only as a\nhash, so a lost key must be replaced rather than recovered.\n\n**The pickup code is never returned by this API.** It opens a locker door, so iBOX\nsends it directly to your customer by SMS in their own language.\n\n**Errors** follow RFC 9457. Branch on `code`, which is stable, rather than `title`,\nwhich is prose. Every error carries a `correlation_id` worth quoting in a support\nrequest.\n\n**Rate limit.** 100 requests per minute per API key, as a sliding window. Over it,\nrequests return `429` with a `Retry-After` header in seconds and\n`code: rate_limit.exceeded`. Back off by `Retry-After` rather than retrying\nimmediately: a refused request still counts against the window."},"servers":[{"url":"https://api.iboxdelivery.com/v1"}],"tags":[{"name":"Stations","description":"Where customers can collect."},{"name":"Shipments","description":"Book and inspect your shipments."},{"name":"Tracking","description":"Progress of your own parcels."}],"paths":{"/stations":{"get":{"tags":["Stations"],"summary":"List locker stations","description":"The stations a customer can be offered at checkout, for rendering a location picker. Only active stations with a cabinet installed are listed, so every id returned here is one `POST /shipments` will accept. `available_sizes` is live — read it when you draw the picker rather than caching it.","operationId":"listStations","security":[{"apiKey":[]}],"parameters":[{"name":"q","in":"query","required":false,"description":"Matches name, city, address or code. Diacritics are ignored.","schema":{"type":"string"},"example":"prishtina"},{"name":"size","in":"query","required":false,"description":"Only stations with a free compartment large enough for this size.","schema":{"type":"string","enum":["S","M","L","XL"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Bookable stations, by city.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Station"}},"limit":{"type":"integer"}},"required":["data","limit"]}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`size` was not one of S, M, L, XL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. See the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shipments":{"post":{"tags":["Shipments"],"summary":"Book a shipment","description":"Creates and books a shipment in one call, returning its tracking number. The destination station must have a locker cabinet installed.","operationId":"createShipment","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateShipment"}}}},"responses":{"201":{"description":"Booked.","headers":{"Location":{"schema":{"type":"string"},"description":"URL of the new shipment."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Shipment"}}}},"400":{"description":"Body was not valid JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed, the station does not exist, or it has no cabinet yet. See `errors[]`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded. See the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Shipments"],"summary":"List your shipments","operationId":"listShipments","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"Newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Shipment"}},"limit":{"type":"integer"}},"required":["data","limit"]}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shipments/{id}":{"get":{"tags":["Shipments"],"summary":"Retrieve a shipment","description":"A shipment belonging to another organization is reported as absent rather than forbidden.","operationId":"getShipment","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The shipment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Shipment"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such shipment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shipments/{id}/label":{"get":{"tags":["Shipments"],"summary":"Download the shipping label","description":"Returns a 100×150mm thermal label as PDF, with the Code 128 parcel barcode the locker scans. The first request for a booked shipment also moves it to `label_created`; reprinting does not transition it again.","operationId":"getShipmentLabel","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The label.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such shipment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The shipment has not been booked yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/track/{trackingNumber}":{"get":{"tags":["Tracking"],"summary":"Track a parcel","description":"Status and history for one of your own parcels, for showing progress on your order page. Returns no personal data and never the pickup code. Authenticated and scoped to your organization: tracking numbers are short enough that an open endpoint would invite walking the range to discover which parcels exist.","operationId":"trackShipment","security":[{"apiKey":[]}],"parameters":[{"name":"trackingNumber","in":"path","required":true,"schema":{"type":"string"},"example":"IBX4820917355"}],"responses":{"200":{"description":"Current state and history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tracking"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No parcel with that tracking number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Your iBOX API key, e.g. `ibx_live_...`."}},"schemas":{"CreateShipment":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"destination_station_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Locker station to deliver to."},"parcel_size":{"type":"string","enum":["S","M","L","XL"],"description":"Compartment size required."},"recipient_name":{"type":"string","minLength":1,"maxLength":120},"recipient_phone":{"type":"string","minLength":5,"maxLength":32,"description":"Where the pickup code is sent. Kosovo format, e.g. +383 44 123 456."},"recipient_email":{"type":"string","maxLength":200,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"recipient_language":{"description":"Language for the customer's notifications. Defaults to Albanian.","type":"string","enum":["en","sq","sr","tr"]},"merchant_order_reference":{"description":"Your own order number, echoed back on every response.","type":"string","minLength":1,"maxLength":64}},"required":["destination_station_id","parcel_size","recipient_name","recipient_phone"],"additionalProperties":false,"description":"A shipment to book."},"Shipment":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"tracking_number":{"type":"string","description":"Share this with your customer."},"parcel_barcode":{"type":"string","description":"Printed on the label; scanned at the locker."},"status":{"type":"string"},"parcel_size":{"type":"string"},"recipient_name":{"type":"string"},"merchant_order_reference":{"anyOf":[{"type":"string"},{"type":"null"}]},"destination":{"type":"object","properties":{"station_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"cabinet_sn":{"type":"string","description":"The manufacturer's serial for the cabinet."},"slot_no":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Compartment, once the parcel is deposited."}},"required":["station_id","cabinet_sn","slot_no"],"additionalProperties":false},"stored_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"retention_deadline_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"picked_up_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","tracking_number","parcel_barcode","status","parcel_size","recipient_name","merchant_order_reference","destination","stored_at","retention_deadline_at","picked_up_at","created_at","updated_at"],"additionalProperties":false,"description":"A shipment as the booking merchant sees it. Never includes the pickup code."},"Station":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"Pass this as `destination_station_id` when booking."},"code":{"type":"string","description":"iBOX's own code for the site, e.g. PRN-01."},"name":{"type":"string"},"city":{"type":"string"},"address":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"opening_hours":{"type":"string","description":"When the cabinet can be reached, for showing beside the location in a picker. Every pilot cabinet stands in a publicly accessible spot, so this is `24/7` network-wide today rather than a per-site timetable."},"available_sizes":{"type":"array","items":{"type":"string","enum":["S","M","L","XL"]},"description":"Parcel sizes this station can still take. A size is listed when a free compartment at least that large exists right now, so it moves as parcels are deposited and collected — read it when you render the picker, not once a day."},"free_compartments":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Compartments free at this station right now."}},"required":["id","code","name","city","address","latitude","longitude","opening_hours","available_sizes","free_compartments"],"additionalProperties":false,"description":"A locker station a customer can choose at checkout."},"Tracking":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"tracking_number":{"type":"string"},"status":{"type":"string","enum":["booked","in_transit","ready_for_pickup","collected","returning","returned","cancelled","exception"],"description":"Stable public status. Internal states are mapped onto these."},"parcel_size":{"type":"string"},"locker":{"type":"object","properties":{"name":{"type":"string"},"city":{"type":"string"},"address":{"type":"string"},"compartment":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["name","city","address","compartment"],"additionalProperties":false},"collect_by":{"anyOf":[{"type":"string"},{"type":"null"}]},"stored_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"collected_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"booked_at":{"type":"string"},"timeline":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string"},"at":{"type":"string"}},"required":["status","at"],"additionalProperties":false}}},"required":["tracking_number","status","parcel_size","locker","collect_by","stored_at","collected_at","booked_at","timeline"],"additionalProperties":false,"description":"The customer-facing view. Carries no personal data and never the pickup code, because anyone holding a tracking number can request it."},"Error":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"status":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"detail":{"type":"string"},"code":{"type":"string","description":"Stable machine-readable code. Branch on this, not the title."},"correlation_id":{"type":"string","description":"Quote this when reporting a problem."},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}},"required":["field","code","message"],"additionalProperties":false}}},"required":["title","status","code","correlation_id"],"additionalProperties":false,"description":"RFC 9457-shaped error."}}}}