API version 2

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 to 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

Cyke offers direct integrations with the main e-commerce platforms. You may use the Shopify and Woocommerce plugins in this documentation.

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 with CMS…)

Cyke offers a zero-code integration that works with the main e-commerce and CMS platforms: Shopify, Wordpress, 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.

Changelog

Upstream changes

These changes have been released directly on the current version of the API because they preserve backwards compatibility.

  • The package object has a new client_reference field to store any ID on the client side, eg. a barcode.
  • The package object contains all the usefull information to describe it: name, volume, weight, temperature and fragility
  • The Package object can be created providing the name, volume and weight instead of the package_type_id

Migrating from v1

V2 has been released on July 10th, 2023. The API v1 has been shut down on May 23rd, 2024.

Here are the main changes:

  • Timeslots: complete_after and complete_before are no longer used on the delivery. Please use slot_starting_at and slot_ending_at on the tasks (pickup and/or dropoff) instead.
  • Packages are no longer sent by their names but by id. You may retrieve your package types list using the API.
  • The API is no longer available at pedalo.olvo.fr, please use www.cyke.io

If you need to have a look at the API v1, you can access the documentation there.

Environments

The Cyke API es available directly on the Cyke main production server at https://www.cyke.io/.

If you need to develop an API integration and do not want your tests to interfere with the production data, you may ask for an access to a test environment. Please send us an email at cyke@cargonautes.fr.

Test server specific features:

  • It is available at https://staging.cyke.io/
  • It never send emails nor text messages since deliveries are fake. (This means you cannot ask for a new password using the “Forgotten password feature”)

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 Packages
  • 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.

Attributes:

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 Packages 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. Refer to the Package and PackageType entities and the code example below to see what is expected from a package.
reversed_packages array of Packages Only taken into account if bring_back_containers param is used.
The packages that should be collected upon delivery.

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:

Attributes:

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.”_

Package

A Package represents a parcel or any kind of merchandise that is to be transported on a Delivery.

Attributes:

Name Type Description
amount integer Mandatory.
The amount of packages of this kind in the delivery.
package_type_id integer The identifier of the package type on Cyke.
name string A name you can provide to identify and describe the package.
volume_dm3 float The quantity represented by the package. In liters.
weight_kg float The weight represented by the package. In kilograms.
fragility enum null, fragile or super_fragile.
temperature enum null, fresh or hot.
client_reference string An identifier of the package. Can be the barcode.

When creating a delivery, you will have to provide either:

  • volume_dm3, weight_kg and name to provide directly the mandatory information OR
  • A package_type_id to use the information from the PackageType

Note: In previous version of the API, you were able to describe a package with its type only, without id. This is no longer the recommended way.

PackageType

PackageTypes are examples of packages that you can use to describe the delivery packages without providing all the data for each package.

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 < 12L”, “medium parcel < 15L”, etc.).

Package types must be agreed beforehand commercially, and configured in the Cyke staging and production environment before you can use them.

Attributes:

Name Type Description
id integer Mandatory.
The identifier of the package type on Cyke.
type string The exact name of the package type, as you can see it in your Cyke account.
volume float The quantity represented by the package type. In liters.
weight float The weight represented by the package type. In kilograms.
fragility enum null, fragile or super_fragile.
temperature enum null, fresh or hot.

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 package types

Endpoint

GET /api/v2/package_types

URL Parameters

None.

Code examples

Request

curl --location --request GET '<URL>/api/v2/package_types' \
  --header 'X-User-Email: victor@my_company.com' \
  --header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
  --data-raw ''

Response

[
  {
    "id": 1,
    "type": "Carton de 12 bières 33cl",
    "volume": 4,
    "weight": 6,
    "fragility": "fragile",
    "temperature": null
  },
  ...
  {
    "id": 5,
    "type": "Fûts inox de 20L",
    "volume": 25,
    "weight": 30,
    "fragility": null,
    "temperature": null
  }
]

List deliveries

Endpoint

GET /api/v2/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/v2/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/v2/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",
    "slot_starting_at": "2020-08-01T11:00:00.000+02:00",
    "slot_ending_at": "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",
    "slot_starting_at": "2020-11-09T16:00:00.000+02:00",
    "slot_ending_at": "2020-11-09T18:00:00.000+02:00",
    "price_cents": null
  }
]

Create a delivery

Endpoint

POST /api/v2/deliveries

Code examples

Request

curl --location --request POST '<URL>/api/v2/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": [
    {
      "name": "Carton de 12 bières 33cl",
      "amount": 3,
      "volume_dm3": 4,
      "weight_kg": 6,
      "fragility": null,
      "temperature": null,
      "client_reference": "1234"
    },
    {
      "package_type_id": 5,
      "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/v2/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": [
    {
      "name": "Carton de 12 bières 33cl",
      "amount": 3,
      "volume_dm3": 4,
      "weight_kg": 6,
      "fragility": null,
      "temperature": null,
      "client_reference": "1234",
    },
    {
      "package_type_id": 5,
      "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/v2/deliveries/<DELIVERY_ID>

Code examples

Request

curl --location --request GET '<URL>/api/v2/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",
  "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": [
    {
      "package_type_id": null,
      "type": "Carton de 12 bières 33cl",
      "name": "Carton de 12 bières 33cl",
      "amount": 3,
      "volume_dm3": 4,
      "weight_kg": 6,
      "fragility": null,
      "temperature": null,
      "client_reference": "1234"
    },
    {
      "package_type_id": 5,
      "type": "Thermobox",
      "name": "Thermobox",
      "amount": 1,
      "volume_dm3": 20,
      "weight_kg": 10,
      "fragility": null,
      "temperature": "fresh",
      "client_reference": null
    }
  ],
  "reversed_packages": []
}

Get the latest delivery

This endpoint retrieves the latest delivery for the given filters.

Endpoint

GET /api/v2/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/v2/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/v2/deliveries/latest?client_order_reference=POE5F29A' \
--header 'X-User-Email: victor@my_company.com' \
--header 'X-User-Token: G6RNXfdNaPPh3DtMYAR8' \
--data-raw ''

Response

Returns the updated Delivery with an http status of 200 (Created). See Get a delivery response.

Update a delivery

Endpoint

PATCH /api/v2/deliveries/<DELIVERY_ID>
PUT /api/v2/deliveries/<DELIVERY_ID>

Code examples

Request

curl --location --request PUT '<URL>/api/v2/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/v2/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/v2/deliveries/<DELIVERY_ID>/cancel

Code examples

Request

curl --location --request PATCH '<URL>/api/v2/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/v2/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"
  ]
}