API V1 (deprecated)
API version 1
Warning: V2 has been released on July 10th, 2023. The API v1 has been shut down on May 23rd, 2024.
Please have a look at the API v2 documentation, which is available there.
Introduction
Cyke is the homebrewed delivery order tool of Cargonautes (ex OLVO). In addition to a graphical interface, it has a simple RESTful API to manage deliveries, targetted mainly at e-commerce websites.
If you’re here, it’s probable your company has already started opening an account with Cargonautes (ex OLVO) (or another delivery company using Cyke) and you already have a precise idea of the delivery services that you’ll need. If it’s not the case, please write to contact@cargonautes.fr. If it’s the case, we’ll arrange by email an account on our integration platform (please don’t test the API on production, as we’ll have real people pedaling behind and you’ll be billed!)
The API has (for now) five endpoints handle your deliveries. It is designed to avoid time-consuming operations when creating deliveries. But for day-to-day management of deliveries, it needs to be coupled to the Cyke graphical interface, where other operations (like understanding billing) are possible.
In addition to these entry endpoints, Cyke also has outgoing webhooks, documented here that can inform your systems back at each delivery change.
Feature requests are very welcomed, feel free to write to cyke@cargonautes.fr for that.
Specificities for e-commerce websites
In comparison to other delivery APIs, Cyke has more required information.
The main difference is that we expect your website to capture and send a precise delivery slot, with a slot start and a slot end (e.g. 2021-01-20 15:00:00
/ 2021-01-20 18:00:00
). It’s your responsability to capture the day and slot of delivery, and communicate it to your customer. Most e-commerce CMS have available plugins in their marketplace to propose and capture this kind of slots, but we can advise plugins for Shopify and Woocommerce if needed.
Please also note that a phone number is a requirement, and that we strongly encourage to capture access information such as entrance code, floor, apartment number, etc, to keep delivery failure rate low.
Zapier plugin (compatible Shopify, Woocommerce…)
This section only applies to Cargonautes (ex OLVO) customers.
Cargonautes (ex OLVO) offers a zero-code integration that works with the main e-commerce CMS: Shopify, Woocommerce, Prestashop… This integration is dealt through a generic Zapier/Cyke application. For now this Zapier plugin is kept private and shared on demand. If you know how to use a Zapier plugin, you can ask an access. If you don’t know how to use a Zapier plugin, we can take care of connecting for you your e-commerce website to Cyke with this plugin, and monitor the integration. Feel free to contact your sales representative if interested.
Authentication protocol
The authentification is done by providing an email and a token in the HTTP header of your request:
X-User-Email: <EMAIL>
X-User-Token: <API_TOKEN>
For example it could be something like:
X-User-Email: victor@my_company.com
X-User-Token: G6RNXfdNaPPh3DtMYAR8
You can get your API token in the general information page in your Cyke Account. From the sidebar: Configuration > General information, and scroll down to the API section to reveal your token.
Entities
Delivery
A Delivery represents the main service sold by Cargonautes (ex OLVO): moving things around from one place to another, during defined timeslots.
- The “things” moved around are DeliveryPackages
- A Delivery is composed of two tasks, a Pickup task, and a Dropoff task. Each task has its own timeslots – slot_starting_at and slot_ending_at
- These Tasks happen at specific Places, that have an address, a recipient, etc.
Cyke has the concept of «default place» for a customer: it defines where you are hosted and where your outbound deliveries are sent from. It can be your own office/store, or it can be a hub owned by Cargonautes (ex OLVO) (in this case, you are responsible for bringing the packages to Cargonautes (ex OLVO).) Task details happening at this default place (usually a Pickup task), be it your own place or Cargonautes (ex OLVO) hub, do not need to be specified in the API creation call and will be automatically filled. When creating a Delivery through the API, you should only specify the details of the Tasks and Places that do happen at the final recipient place (your own client). That means that for most of the Deliveries, you only send a Dropoff task and its place. If you need an inbound Delivery where a messenger picks up a package and brings it to you, you’ll need only to specify a Pickup object and Place.
The delivery time slot is represented by two fields, slot_starting_at
and slot_ending_at
on the Tasks. As only the main Task (usually a dropoff) needs to be specified, the timeslot of the Task happening at your own place is automatically calculated. The API accepts most timeslots, unless they are already started. It is the client’s responsibility to implement the business rules linked to the desired time slot size, anticipation time, and opening hours and days – knowing that reduced slot size and anticipation can induce higher prices.
Parameters:
Name | Type | Description |
---|---|---|
sender_display_name |
string |
Optional. If you want to deliver a parcel in the name of another name than yours, you can write it here. If given, this is the name that the recipient will see in the SMS that is sent to him, instead of your company’s name. |
comments |
string |
Optional. Any comments you could make about the delivery to help our staff. |
client_order_reference |
string |
Optional. An internal order reference of yours, ( typically your own purchase id for this delivery). You can later retrieve a delivery using this reference. |
bring_back_containers |
boolean |
Optional. If the messenger needs to bring back deposited containers (empty bottle, etc). If not specified, the company default is applied. |
dropoff |
Task |
Optional as long as you give a pickup. The address and recipient information where to deliver. |
pickup |
Task |
Optional as long as you give a dropoff. The address and recipient information where to pick the goods up. |
packages |
array of PackageTypes |
Should at least contain one Package. The packages that your delivery contains. When updating packages, you must provide the whole array of packages as it will override the existing packages. |
When getting an existing Delivery, you will also get access to additionnal fields:
Name | Type | Description |
---|---|---|
status |
string enum:saved ,scheduled ,picked_up ,delivered ,cancelled ,failed
|
The status of the delivery. saved means the delivery has been saved to our dispatch system and is currently not assigned to anybody. scheduled means the task has been assigned to one of our workers (the worker may not have started the task yet, though). picked_up is for deliveries composed of one pickup and one dropoff: it means the goods have been picked up by us, and are now waiting to be delivered. If the goods have been successfully delivered, the status is delivered , otherwise if the delivery failed, the status is failed . If the task has been cancelled, the status is cancelled . |
price_cents |
integer | If we include some pricing functionality for your company in Cyke, the price_cents would show the price of the delivery in cents. Otherwise, it will be equal to null . |
direction |
string enum:outbound ,inbound ,other
|
Tells if the delivery’s main task is the dropoff (outbound ), the pickup (inbound ), or both the pickup and the dropoff (other ). |
original_delivery_id |
integer |
Optional. Identifier of the original delivery if this delivery is a redelivery. |
redelivery_id |
integer |
Optional. Identifier of the redelivery if this delivery has one. |
Task
A Task can be either a saved address that you have already in your account, or a place. In the first case you should give the Task’s unique number in this field:
{
...
"pickup": 23,
...
}
Otherwise, juste give a JSON with an argument place
inside:
{
...
"pickup": {
"slot_starting_at": "2019-09-04T16:00:00.000+02:00",
"slot_ending_at": "2019-09-04T18:00:00.000+02:00",
"place": {
"recipient_name": "Cercei Lannister",
"recipient_phone": "+33670707070",
"address": "1 place du Palais Royal",
"postal_code": "75001",
"city": "Paris",
}
},
...
}
When getting an existing Task, you will also get access to the following fields:
Name | Type | Description |
---|---|---|
slot_starting_at |
string |
Mandatory. The time after which the task should be completed. Has to be written in GMT format. Example: “2020-11-09T16:00:00.000+02:00” |
slot_ending_at |
string |
Mandatory. The time before which the task should be completed. Has to be written in GMT format. Example: “2020-11-09T18:00:00.000+02:00” |
tracking_url |
string | A public url your customers can use to track the messenger delivering their packages. Available once the delivery has started |
completed_at |
string | The datetime at which the delivery has been completed (delivered of failed )Example: “2020-11-09T18:00:00.000+02:00”” |
failure_reason |
string | The reason why the delivery task failed. Chosen by the messenger in their application. |
notes |
string | Notes filled freely by the messenger when they completed the task. |
place |
Place | See below at Place |
completion_signature |
Object | A url to access the recipient signature and accepted_by containing their name. Available once the delivery has been delivered |
completion_pictures |
Array | A collection of url to access the pictures. Available once the delivery has been delivered |
Place
A Place is basically an address with some recipient information:
Parameters:
Name | Type | Description |
---|---|---|
recipient_name |
string |
Mandatory. The recipient’s name and surname. When shipping to a company, give the operations manager’s name and phone. |
recipient_phone |
string |
Mandatory, except for some particular cases (contact us for more information). The recipient’s phone number. International format is strongly recommended. A tracking link will be sent to this number when the courier starts the task. Example: “+32485549268” |
recipient_email |
string |
Optional. The recipient’s email address. In the future, a tracking link may be sent to this address before the courier arrives. Example: “tartempion@example.org” |
company_name |
string |
Optional. The company’s name when shipping to an office, a shop, etc. |
address |
string |
Mandatory. The number and street name. Don’t give any other information here (it will fail otherwise). Instead, keep them for address_instructions .Example: “1 place du Palais Royal” |
postal_code |
string |
Mandatory. The postal code of the address. Should be in the zone that we deliver in. Please contact us so we can give you the list o accepted locations. Example: “75000” |
city |
string |
Mandatory. The city name. |
address_instructions |
string |
Optional but highly recommended. Any information that could help the courier reach the recipient’s front door. Access code, building number, interphone, alley, floor, apartment number, digicode, etc. Example: « digicode 3495 on your left inside.”_ |
PackageType
PackageTypes aim to describe what’s inside your delivery.
Each customer account has a list of accessible package types or size categories. PackagesTypes will describe objects that are delivered (ie. “Bottle 1.5L”, “Flower bunch size M”, etc.), while size categories will rather describe the size of a delivery – not what’s inside (ie. “small parcel < 12dm3”, “medium parcel < 15dm3”, etc.).
Package types must be agreed beforehand commercially, and configured in the Cyke staging and production environment before you can use them.
Parameters:
Name | Type | Description |
---|---|---|
type |
string |
Mandatory. The exact name of the package type, as you can see it in your Cyke account. Please ask us if you need the list of package types you have. |
amount |
integer |
Mandatory. The amount of packages belonging to this package type in the delivery. If you have custom packages, state the quantity you have for each of them. If you use size categories as packages, only give one package type, and set the quantity to 1. |
API Requests
The Cyke API allows you to either list your deliveries, create a delivery, get a delivery, edit a delivery or cancel a delivery.
List deliveries
Endpoint
GET /api/v1/deliveries
URL Parameters
Parameter | Type | Examples | Description |
---|---|---|---|
client_order_reference |
string |
client_order_reference=PO5EF29A |
Filter deliveries having a specific client order reference. |
Code examples
Request
curl --location --request GET '<URL>/api/v1/deliveries' \
--header 'X-User-Email: victor@my_company.com' \
--header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
--data-raw ''
If you want to apply specific filters:
curl --location --request GET '<URL>/api/v1/deliveries?client_order_reference=POE5F29A' \
--header 'X-User-Email: victor@my_company.com' \
--header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
--data-raw ''
Response
[
{
"id": 1,
"created_at": "2020-07-25T11:50:00.000+02:00",
"status": "delivered",
"complete_after": "2020-08-01T11:00:00.000+02:00",
"complete_before": "2020-08-01T13:00:00.000+02:00",
"price_cents": null
},
...
{
"id": 86,
"created_at": "2020-11-09T10:00:00.000+02:00",
"status": "scheduled",
"complete_after": "2020-11-09T16:00:00.000+02:00",
"complete_before": "2020-11-09T18:00:00.000+02:00",
"price_cents": null
}
]
Create a delivery
Endpoint
POST /api/v1/deliveries
Code examples
Request
curl --location --request POST '<URL>/api/v1/deliveries/' \
--header 'X-User-Email: victor@my_company.com' \
--header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
--header 'Content-Type: application/json' \
--data-raw '{
"dropoff": {
"slot_starting_at": "2020-11-09T16:00:00.000+02:00",
"slot_ending_at": "2020-11-09T18:00:00.000+02:00",
"place": {
"recipient_name": "Cercei Lannister",
"recipient_phone": "+33670707070",
"company_name": "Lannister Inc.",
"address": "1 place du Palais Royal",
"postal_code": "75001",
"city": "Paris",
"address_instructions": "digicode 3495 au fond à gauche"
}
},
"packages": [
{
"type": "Carton de 12 bières 33cl",
"amount": 3
},
{
"type": "Fût inox de 20L",
"amount": 1
}
],
"comments": "Fûts de blonde, faire attention à la DLUO",
"client_order_reference": "POE5F29A",
"bring_back_containers": false
}'
require "uri"
require "net/http"
require "json"
url = URI("https://<URL>/api/v1/deliveries")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-User-Email"] = "victor@my_company.com"
request["X-User-Token"] = "G6RNXfdNaPPh3DtMYAR8"
request["Content-Type"] = "application/json"
delivery = {
"dropoff": {
"slot_starting_at": "2020-11-09T16:00:00.000+02:00",
"slot_ending_at": "2020-11-09T18:00:00.000+02:00",
"place": {
"recipient_name": "Cercei Lannister",
"recipient_phone": "+33670707070",
"company_name": "Lannister Inc.",
"address": "1 place du Palais Royal",
"postal_code": "75001",
"city": "Paris",
"address_instructions": "digicode 3495 au fond à gauche"
}
},
"packages": [
{
"type": "Carton de 12 bières 33cl",
"amount": 3
},
{
"type": "Fût inox de 20L",
"amount": 1
}
],
"comments": "Fûts de blonde, faire attention à la DLUO",
"client_order_reference": "POE5F29A",
"bring_back_containers": false
}
request.body = delivery.to_json
response = https.request(request)
puts response.read_body
Response
Returns the created Delivery with an http status of 201 (Created). See Get a delivery response.
If there is an error during the delivery creation, it will be returned instead of the delivery, and the http status will be 422 (Unprocessable Entity).
For example:
{
"errors": [
"Packages Unknown type"
]
}
Get a delivery
Endpoint
GET /api/v1/deliveries/<DELIVERY_ID>
Code examples
Request
curl --location --request GET '<URL>/api/v1/deliveries/86' \
--header 'X-User-Email: victor@my_company.com' \
--header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
--data-raw ''
Response
{
"id": 86,
"created_at": "2020-11-09T10:00:00.000+02:00",
"status": "saved",
"complete_after": "2020-11-09T16:00:00.000+02:00",
"complete_before": "2020-11-09T18:00:00.000+02:00",
"comments": "Fûts de blonde, faire attention à la DLUO",
"client_order_reference": "POE5F29A",
"price_cents": null,
"direction": "outbound",
"bring_back_containers": false,
"original_delivery_id": null,
"redelivery_id": null,
"dropoff": {
"slot_starting_at": "2020-11-09T16:00:00.000+02:00",
"slot_ending_at": "2020-11-09T18:00:00.000+02:00",
"tracking_url": null,
"completed_at": null,
"failure_reason": null,
"notes": null,
"completion_signature": null,
"completion_pictures": [],
"place": {
"recipient_name": "Cercei Lannister",
"recipient_phone": "+32 470 70 70 70",
"company_name": "Lannister Inc.",
"address": "1 place du Palais Royal",
"postal_code": "75001",
"city": "Paris",
"address_instructions": "digicode 3495 au fond à gauche"
}
},
"packages": [
{
"type": "Carton de 12 bières 33cl",
"amount": 3
},
{
"type": "Fût inox de 20L",
"amount": 1
}
]
}
Get the latest delivery
This endpoint retrieves the latest delivery for the given filters.
Endpoint
GET /api/v1/deliveries/latest
URL Parameters
Parameter | Type | Examples | Description |
---|---|---|---|
client_order_reference |
string |
client_order_reference=PO5EF29A |
Filter deliveries having a specific client order reference. |
Code examples
Request
curl --location --request GET '<URL>/api/v1/deliveries/latest' \
--header 'X-User-Email: victor@my_company.com' \
--header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
--data-raw ''
If you want to apply specific filters:
curl --location --request GET '<URL>/api/v1/deliveries/latest?client_order_reference=POE5F29A' \
--header 'X-User-Email: victor@my_company.com' \
--header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
--data-raw ''
Response
{
"id": 86,
"created_at": "2020-11-09T10:00:00.000+02:00",
"status": "saved",
"complete_after": "2020-11-09T16:00:00.000+02:00",
"complete_before": "2020-11-09T18:00:00.000+02:00",
"comments": "Fûts de blonde, faire attention à la DLUO",
"client_order_reference": "POE5F29A",
"price_cents": null,
"direction": "outbound",
"bring_back_containers": false,
"original_delivery_id": null,
"redelivery_id": null,
"dropoff": {
"slot_starting_at": "2020-11-09T16:00:00.000+02:00",
"slot_ending_at": "2020-11-09T18:00:00.000+02:00",
"tracking_url": null,
"completed_at": null,
"failure_reason": null,
"notes": null,
"completion_signature": null,
"completion_pictures": [],
"place": {
"recipient_name": "Cercei Lannister",
"recipient_phone": "+32 470 70 70 70",
"company_name": "Lannister Inc.",
"address": "1 place du Palais Royal",
"postal_code": "75001",
"city": "Paris",
"address_instructions": "digicode 3495 au fond à gauche"
}
},
"packages": [
{
"type": "Carton de 12 bières 33cl",
"amount": 3
},
{
"type": "Fût inox de 20L",
"amount": 1
}
]
}
Update a delivery
Endpoint
PATCH /api/v1/deliveries/<DELIVERY_ID>
PUT /api/v1/deliveries/<DELIVERY_ID>
Code examples
Request
curl --location --request PUT '<URL>/api/v1/deliveries/<DELIVERY_ID>' \
--header 'X-User-Email: victor@my_company.com' \
--header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
--header 'Content-Type: application/json' \
--data-raw '{
"dropoff": {
"slot_starting_at": "2020-11-09T16:30:00.000+02:00"
}
}'
require "uri"
require "net/http"
require "json"
url = URI("https://<URL>/api/v1/deliveries/<DELIVERY_ID>")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Put.new(url)
request["X-User-Email"] = "victor@my_company.com"
request["X-User-Token"] = "G6RNXfdNaPPh3DtMYAR8"
request["Content-Type"] = "application/json"
delivery = {
"dropoff": {
"slot_starting_at": "2020-11-09T16:30:00.000+02:00"
},
}
request.body = delivery.to_json
response = https.request(request)
puts response.read_body
Response
Returns the updated Delivery with an http status of 200 (Created). See Get a delivery response.
If there is an error during the delivery update, it will be returned instead of the delivery, and the http status will be 422 (Unprocessable Entity).
For example:
{
"errors": [
"Packages Unknown type"
]
}
Cancel a delivery
Endpoint
PATCH /api/v1/deliveries/<DELIVERY_ID>/cancel
Code examples
Request
curl --location --request PATCH '<URL>/api/v1/deliveries/<DELIVERY_ID>/cancel' \
--header 'X-User-Email: victor@my_company.com' \
--header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
--header 'Content-Type: application/json'
require "uri"
require "net/http"
require "json"
url = URI("https://<URL>/api/v1/deliveries/<DELIVERY_ID>")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["X-User-Email"] = "victor@my_company.com"
request["X-User-Token"] = "G6RNXfdNaPPh3DtMYAR8"
request["Content-Type"] = "application/json"
response = https.request(request)
puts response.read_body
Response
Returns the canceled Delivery with an http status of 200 (Created). See Get a delivery response.
If there is an error during the delivery cancellation, it will be returned instead of the delivery, and the http status will be 422 (Unprocessable Entity).
For example:
{
"errors": [
"Packages Unknown type"
]
}