API version 1 English version 🇬🇧

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 user preference page in your Cyke Account.

Entities

Delivery

A Delivery represents the main service sold by Cargonautes (ex OLVO): moving things around from one place to another, during defined timeslots.

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 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. Filled by the messenger
notes string Notes filled by the messenger when they completed the task
place Place See below at Place
completion_signature Array 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 20 minutes before the courier arrives.

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
PUT /api/v1/deliveries/<DELIVERY_ID>/cancel

Code examples

Request

curl --location --request PUT '<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::Put.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"
  ]
}