Skip to content

Instantly share code, notes, and snippets.

@skyjur
Created June 11, 2020 11:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skyjur/7d3114fc1e5dbb0c0e9019020f3f54e7 to your computer and use it in GitHub Desktop.
Save skyjur/7d3114fc1e5dbb0c0e9019020f3f54e7 to your computer and use it in GitHub Desktop.
Orkestro API
FORMAT: 1A
HOST: https://api.orkestro.io/
# Orkestro API
Orkestro's public API enables customers to programatically integrate Orkestro's marketplace capabilities into their own platform.
API consumers are able to submit orders, get their statuses, cancel them and get constant status updates for them through webhooks.
## Glossary
- **Order** - Represents a logical unit of work to get a parcel from location A to B using Orkestro's Platform.
- **Delivery** - Represents an attempt to fulfill a single order. To complete an order and to fulfill it's requirements one ore more deliveries might be required. In general a single delivery will be used for an Order however in case of forgotten items, high urgency deliveries, maybe multiple deliveries will be created.
- **Status Update** - Represents the change of state in an order or a delivery. Typical updates include status changes (InTransit, ArrivedAtDropoff, etc..) and geo-location updates.
## Authorisation [/users/sign-in]
In order to make requests to any of the public Orkestro API endpoints, you will need to have either an access token or an api key.
Each request must include this token or api key under the `Bearer` realm in the `Authorization` header. For example:
#### Headers
- **Authorization:** Bearer [INSERT ACCESS TOKEN]
- **api-key:** [INSERT API KEY]
* **API Key**: An API can be obtained from your account manager. This will be valid until revoked.
* **Access Token**: An access token can be obtained from the below request username/password request
### Sign-in [POST]
When you sign in with your username and password, you will receive in return an access token and a refresh token.
Passwords should be base64 encoded and at least 8 characters long before encoding.
* AccessToken: Expires within 5 minutes of being generated
* RefreshToken: Can be exchanged for a new access token. Does not expire.
+ Request (application/json)
{
"username": "INSERT YOUR USERNAME HERE",
"password": "INSERT YOUR BASE64 ENCODED PASSWORD HERE",
}
+ Response 200 (application/json)
{
"accessToken": "eyJ...",
"refreshToken" "Eja...",
"expiresAt": 1566455122
}
### Refreshing an access token [/users/refresh-token]
In order to avoid having to sign-in when an access token expires, you can use the returned refresh token in the sign in request and exchange it for a new access token
You will receive the same response payload as the one received when signing in a username and password.
+ Request (application/json)
{
"refreshToken": "INSERT YOUR REFRESH TOKEN HERE"
}
+ Response 200 (application/json)
{
"accessToken": "eyJ...",
"refreshToken" "Eja...",
"expiresAt": 1566455122
}
## Create an order [/create-orders]
### Create an order [POST /orders]
To create a new order through the API the payload must contain the most accurate infromation regarding the delivery.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|pickup|Yes|Pickup|Details and instruction for the pickup location|
|dropoff|Yes|Dropoff|Details and instruction for the pickup location|
|parcel|Yes|Parcel|Details of the parcel and its contents|
|schedule|Yes|Schedule|Describes when the pickup and the dropoff should happen|
|callbackUrl|No|String|Webhook Url where the status updated events should be posted|
### Pickup
Defines the location where the package should be picked up by the fleet.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|name|Yes|String|Full Name of the contact at the pickup location|
|companyName|No|String|Company name at the pickup location|
|addressLine1|Yes|String|House number and street for the pickup|
|addressLine2|No|String|Flat number and/or additional address components|
|city|Yes|String|City of the pickup|
|postCode|Yes|String|Post Code of the pickup|
|country|Yes|String|Country of the pickup|
|phone|Yes|String|Phone number of the pickup contact|
|email|Yes|String|E-mail address of the pickup contact|
|instructions|No|String|Simple pickup instructions for the driver|
|location|No|Location|Geo-location of the address|
#### Location
In some cases it might be more accurate if the coordinates for the pickup location are provided as part of the pickup or the dropoff object.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|lat|No|Float|Lattitude|
|long|No|Float|Longitude|
### Dropoff
Defines the location where the package should be delivered to by the fleet.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|name|Yes|String|Full Name of the contact at the pickup location|
|companyName|No|String|Company name at the pickup location|
|addressLine1|Yes|String|House number and street for the pickup|
|addressLine2|No|String|Flat number and/or additional address components|
|city|Yes|String|City of the pickup|
|postCode|Yes|String|Post Code of the pickup|
|country|Yes|String|Country of the pickup|
|phone|Yes|String|Phone number of the pickup contact|
|email|Yes|String|E-mail address of the pickup contact|
|instructions|No|String|Simple pickup instructions for the driver|
|location|No|Location|Geo-location of the address|
### Schedule
Defines when the pickup and the dropoff can and should happen.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|type|Yes|String|Type of the schedule. Valid values are *urgent, scheduled*|
|pickupWindow|No\*|TimeSlot|Time window for pickup|
|dropoffWindow|No\*|TimeSlot|Time window for dropoff|
\* When the schedule `type` is set to `urgent`, pickup and dropoff windows are optional and won't be considered. When the delivery `type` is set to `scheduled` then `pickupWindow.start` and `dropoffWindow.start` values are required.
An order will be converted to `urgent` if the start of the `pickupWindow` is less than 15 minutes after the time an order is submitted.
#### TimeSlot
Defines when the pickup and the dropoff can and should happen.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|start|No|String|Start of the timeslot. DateTime must be in **ISO8601** format.|
|end|No|String|End of the timeslot. DateTime must be in **ISO8601** format.|
### Parcel
The parcel can be defined in detail. This information helps the Operations Teams and our algorithms to request accurate quotes and book the right vehicles while providing sufficient information for the driver to guarantee your parcel gets delivered without any surprises.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|handlingInstructions|Yes|Array|Array of Strings. Possible individual values are: *fragile, do_not_rotate, contains_liquid, contains_hot_food*. The array can be empty, however we do encourage you to always specify the most thorough instructions for your parcel|
|dimensions|No|ParcelDimension|Defines the physical parameters (size and weight) of the parcel|
|items|Yes|Array|Array of ParcelItem objects. Describes the contents of the parcel. Empty arrays are permitted.|
|reference|Yes|String|Parcel reference will be provided to the driver. This value is used by them at the pickup and the dropoff locations.|
|value|Yes|ParcelValue|Estimated value of the parcel.|
#### ParcelDimension
Understanding the phyisical parameters of the parcel plays an important role of selecting the most cost-efficient vehicle.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|weightKg|No|float|Weight of the parcel in kilograms.|
|widthCm|No|integer|Width of the parcel in centimetres.|
|heightCm|No|integer|Height of the parcel in centimetres.|
|lengthCm|No|integer|Length of the parcel in centimetres.|
#### ParcelItem
Understanding the content of the delivery allows for further optimisation in the delivery process.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|name|Yes|String|Free text format name of the product. *Example: Type-OO flour*|
#### ParcelValue
Based on the value of the parcel Operation Teams and our algorithm might prefer selected fleets.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|amount|Yes|float|Value of the parcel|
|currency|Yes|String|Currency in which the amount was specified. Supported values: *gbp*|
### Create Order Response
The HTTP Response should be `201` in case the Order was successfully created. The body of the response will be an object with a single `id` field. The ID represents the OrderID that has just been created.
#### Error codes
|Status|Description|
|-----|-----------|
|400|The request did not pass validation. See body for details.|
|401|The request did not contain an authorisation header and / or the token was not valid|
|403|The user does not have access rights for the requested resource / operation.|
|500|An internal error happened. Please contact the Orkestro Team if the problem persists.|
In case of 400 errors, you'll get detailed information of the validation problems.
The structure will be
```
{
"errors": {
"fieldName": [
"error message"
]
}
}
```
### Status Updates
Whenever during the order creation process the callbackUrl is provided status updates will be sent to the specified endpoint whenever a change happens related to that order.
**Note**: The same update might arrive multiple times.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|orderId|Yes|String|ID of the order|
|deliveryId|Yes|String|ID of the delivery|
|orderStatus|Yes|OrderStatus|Status of the order, see Order Status enums|
|deliveryStatus|Yes|DeliveryStatus|Status of the delivery, see Delivery Status enums|
|eta|No|ETA|Driver's ETA for pickup and dropoff locations|
|vehicleType|Yes|VehicleType|The type of vehicle being used, see Vehicle Type enums|
|driver|No|Driver|Driver's information|
|createdAt|Yes|String|Timestamp of when the status update was created in **ISO8601** format
#### ETA
When ETAs are provided dropoff location is usually returned back from the third party system / driver. In case the data is missing we are calculating it based on driver's current location and the destination location.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|pickup|No|String|ETA for pickup time in **ISO8601** format|
|dropoff|Yes|String|ETA for pickup time in **ISO8601** format|
#### Driver
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|name|Yes|String|Driver's full name|
|phone|No|String|Driver's phone number|
|location|No|Location|Driver's current location|
**Example status update**
```
{
"orderId": "",
"deliveryId: "",
"orderStatus": "in_progress",
"deliveryStatus": "in_transit",
"fleet": {
"name": "Addisone Lee"
},
"driver": {
"name": "John Doe",
"phone": "+441234567890",
"location": {
"lat": "50.0934567",
"long": "-0.9234567"
}
},
"eta": {
"pickup": "2019-07-23T08:14:26.859Z",
"dropoff": "2019-07-23T10:14:26.859Z"
},
"vehicleType": "car"
}
```
+ Request (application/json)
+ Headers
Authorization: Bearer [INSERT TOKEN HERE]
+ Body
{
"pickup": {
"name": "John Doe",
"companyName": "ACME LTD",
"addressLine1": "41 Provost Street",
"city": "London",
"postCode": "E20 1HT",
"country": "United Kingdom",
"phone": "+441234567890",
"instructions": "Pick up package from reception",
"email": "john.doe@example.com",
"location": {
"lat": -9.1234567,
"long": 40.1234567
}
},
"dropoff": {
"name": "John Doe",
"companyName": "ACME LTD",
"addressLine1": "2 Glasshouse Gardens",
"city": "London",
"postCode": "E15 4HE",
"country": "United Kingdom",
"phone": "+441234567890",
"instructions": "Please leave the package at the reception",
"email": "john.doe@example.com",
"location": {
"lat": -9.1234567,
"long": 40.1234567
}
},
"parcel": {
"handlingInstructions": [
"contains_liquid"
],
"dimensions": {
"weightKg": 1,
"widthCm": 10,
"heightCm": 30,
"lenghtCm": 10
},
"items": [
{
"name": "Mineral Water"
}
],
"reference": "WATER_ORDER_01",
"value": {
"amount": 3.50,
"currency": "gbp"
}
},
"schedule": {
"type": "urgent"
},
"callbackUrl": "https://api.dev.orkestro.io/status/webhooks_test/ABCD"
}
+ Response 201 (application/json)
{
"id": "bc602d69-a17c-4489-a9e3-446baa4551c4"
}
## Get an order [/get-order]
### Get an order [GET /orders/{id}]
Retrieve an order by it's ID. The returned structure will reflect how Orders and Deliveries are connected. Usually a single submitted order will have one delivery. In some edge cases however you might have multiple deliveries in a single order.
Typical examples would be
* Missing items require another fleet to be booked
* Urgency requires operations to book multiple fleets
* Contents of parcel / quantity requires multiple fleets to be booked.
* Driver failed to complete delivery, another delivery has been created
### Order Request
|Field in Path|Required|Type|Description|
|-----|-----------|--------|----|
|id|Yes|String|Unique identifier for the order|
### Order Response
The requested order with the created deliveries will be returned.
|Field|Required|Type|Description|
|-----|-----------|--------|----|
|id|Yes|String|Value of the parcel|
|status|Yes|String|State of the Order|
|deliveries|Yes|Array|Array of Deliveries representing the attempts to fulfill the order|
|createdAt|Yes|String|DateTime of when the order was created in **ISO8601** format|
|modifiedAt|No|String|DateTime of when the order was last updated in **ISO8601** format.|
#### Order and Delivery Status
Both Orders and individual deliveries will have statuses. Orders have more high level statuses that relates to the overall submission, while deliveries have more detailed statuses expanding on the details of the process of delivery.
**Delivery Statuses**
|Status|Description|
|------|-----------|
|pending|The delivery has been created but has not been quoted yet.|
|looking_for_driver|The delivery has been booked. Waiting for driver to be assigned.|
|driver_en_route_to_pickup|Driver has accepted the delivery and is enroute to pickup location.|
|driver_at_pickup|Driver has arrived to the pickup location and waiting.|
|in_transit|Driver is enroute to the dropoff location with the parcel.|
|driver_at_dropoff|Driver has arrived to the dropoff location and waiting.|
|success|Driver has successfully delivered the parcel.|
|failed|Delivery was not successful. See status updates for further information.|
**Order Statuses**
|Status|Description|
|------|-----------|
|pending|Order has been created and all associated deliveries are pending.|
|in_progress|At least one delivery is already booked.|
|success|Associated delivery has been successfully completed and all other deliveries are in final state.|
|failed|Order failed due to all associated deliveries being in final state and none of those are success.|
|cancelled|Order has been cancelled by owner.|
**Vehicle Types**
|Type|Description|
|----|-----------|
|bicycle|Regular bicycle|
|bicycle_cargo|Cargo bike|
|motorcycle|Motorcycle|
|car|Car|
|van_small|Small van|
|van|Regular size van|
|walk|Walked by a person|
#### Error codes
|Status|Description|
|-----|-----------|
|404|No order found with such ID|
|401|The request did not contain an authorisation header and / or the token was not valid|
|403|The user does not have access rights for the requested resource / operation.|
|500|An internal error happened. Please contact the Orkestro Team if the problem persists.|
+ Parameters
+ id (string) ... Order ID
+ Request (application/json)
+ Headers
Authorization: Bearer [INSERT TOKEN HERE]
+ Response 201 (application/json)
{
"id": "95c278a3-ed82-4988-829b-bb8df622ae50",
"createdAt": "2019-07-16T17:05:07.827324Z",
"modifiedAt": "2019-07-16T17:05:07.827324Z",
"status": "pending"
"deliveries": [
{
"id": "387f658d-dd2b-4ec4-be2f-0cba54fb654c",
"createdAt": "2019-07-16T17:05:07.829049Z",
"modifiedAt": "2019-07-16T17:05:07.829049Z",
"status": "pending"
"schedule": {
"type": "urgent"
},
"pickup": {
"name": "John Doe",
"companyName": "ACME LTD",
"addressLine1": "41 Provost Street",
"city": "London",
"postCode": "E20 1HT",
"country": "United Kingdom",
"phone": "+441234567890",
"instructions": "Pick up package from reception",
"email": "john.doe@example.com",
"location": {
"lat": -9.1234567,
"long": 40.1234567
}
},
"dropoff": {
"name": "John Doe",
"companyName": "ACME LTD",
"addressLine1": "2 Glasshouse Gardens",
"city": "London",
"postCode": "E15 4HE",
"country": "United Kingdom",
"phone": "+441234567890",
"instructions": "Please leave the package at the reception",
"email": "john.doe@example.com",
"location": {
"lat": -9.1234567,
"long": 40.1234567
}
},
"parcel": {
"handlingInstructions": [
"contains_liquid"
],
"dimensions": {
"weightKg": 1,
"widthCm": 10,
"heightCm": 30,
"LenghtCm": 10,
},
"items": [
{
"name": "Mineral Water"
}
],
"reference": "WATER_ORDER_01",
"value": {
"amount": 3.50,
"currency": "gbp"
}
}
}
]
}
## Cancel an order [/cancel-order]
### Cancel an order [POST /orders/{id}/cancel]
Any order can be cancelled unless it's already in a final state. (success, failed, cancelled). However when the order is already in progress an additional charge will be applied.
### Order Request
|Field in Path|Required|Type|Description|
|-----|-----------|--------|----|
|id|Yes|String|Unique identifier for the order|
### Order Response
The cancellation process is asynchronous. The response will have an empty body with a `202` status code. The completion of the cancellation can be tracked by requesting the order by ID or by subscribing to status updates in which case real-time information will be provided through webhooks.
#### Error codes
|Status|Description|
|-----|-----------|
|404|No order found with such ID|
|401|The request did not contain an authorisation header and / or the token was not valid|
|403|The user does not have access rights for the requested resource / operation.|
|500|An internal error happened. Please contact the Orkestro Team if the problem persists.|
+ Parameters
+ id (string) ... Order ID
+ Request
+ Headers
Authorization: Bearer [INSERT TOKEN HERE]
+ Response 202
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment