Skip to main content

How to integrate with a delivery operator powered by MotionTools

For developers that want to programmatically push orders to a delivery operator that is powered by MotionTools.

Support avatar
Written by Support
Updated yesterday

This article is for developers that want to programmatically push orders to a delivery operator that is powered by MotionTools.

Overview

To push an order to a MotionTools powered delivery operators you will make use of the MotionTools REST API. Additional resources & guides can be found in our Help Center.

MotionTools is a multi-tenant platform where each delivery operator has their own tenant. You will push orders to your delivery operator via an account with the customer role.

  • If you know the url to the tenant of your delivery operator, then you can create a customer account yourself.

  • alternatively, please contact your delivery operator to obtain a customer account.

You can push orders by creating either Bookings or Packages via the API. When you create a booking or a package you will receive as response a booking/package object that represents your order in MotionTools. Learn more with out in-depth articles:

This document describes integrating bookings.

Behind the scenes, either automatically or manually, one or more orders (bookings or packages) will then be grouped into optimized Tours. Once in a tour, delivery operator workers are able to start executing your order.

You can track the progress of your order in two ways. You can:

  • listen to Webhooks that are relevant to your use-case. Complete list Here ↗️.

  • periodically fetch the booking or package you’re interested in, using the MotionTools API

As mentioned above, you will interact with the MotionTools API via an account that has the customer role. Certain API endpoints or actions require the admin role. Such actions are marked as such throughout this article. Please contact your delivery operator or MotionTools support to assist you with those. More about roles within MotionTools Here ↗️

To create a connection between the booking or package in MotionTools and the order in your system you should use our Metadata feature.


Things to consider

Modeling your orders: Bookings vs Packages

Certain operators operate using only bookings, others only packages. Please make sure your ideal workflow is supported by the delivery operator as well.

🚴‍♂️ Bookings:

A booking is a request from a Customer to complete a Service. […] It can include one or multiple Stops. A Stop is a task to be completed at a specific location.

📦 Packages:

A package is an individually trackable item that typically has its own properties like size or weight, comes with a scannable label and can be added onto a stop as a pickup or drop-off task.

In order to be able to use packages, your delivery operator needs to have the Packages extension enabled on their tenant.

Although there is some overlap between the two concepts, bookings are generally chosen when the goal is to track tasks or short deliveries whereas packages are better suited to track items or to model large deliveries, with tens or hundreds of packages delivered within a route.

When it comes down to the integration effort, bookings are easier to get started with due to their relative simplicity compared to packages. Packages, as mentioned above, come with additional properties (size, weight), concepts (scannable label), interaction & tracking flows, pricing, lifecycle, etc. There are all useful when modeling large parcel deliveries, but not always needed for short deliveries.

So while it is possible to model a simple restaurant → customer delivery using packages, where the meal bag is represented as a package within MotionTools, we generally recommend choosing bookings for this particular use case since the focus is on performing the delivery, not on the the meal bag itself.

The rest of this article will focus on Bookings.

Scheduling modes: Instant vs Scheduled

When you create a booking you can specify a delay, or lead time, between the moment the booking gets created and the moment the delivery operator is expected to start executing it.

Currently two options are available: instant & scheduled bookings:

  • Instant bookings: indicate that the booking should start executing right away, without any lead time

  • Scheduled bookings: indicate they should be started at some point in the future, known as the scheduled_at time.

More details here.

Granular, per stop, time-windows & ETA’s

MotionTools allows you to specify in great detail the time interval your delivery partner should to individual stops (earliest_arrival_at, latest_arrival_at), and also the time you expect your delivery partner to spend per individual stop(custom_expected_duration_seconds). Refer to the Create Booking Endpoint request payload for details, in the booking.stops model.

Furthermore, for each stop you will get up to date ETA’s via dedicated properties on the stop: expected_arrival_at, expected_departure_at. You can also obtain ETA’s using the API.

Booking lifecycle

It’s important to clearly distinguish the 3 main constructs that model an order & its progress in MotionTools: Booking, Stop, Tour.

The overall flow is the following:

  • via the API you will create a booking using an account with the customer role

  • depending on the scheduling mode and the dispatch configuration, the booking will then be dispatched into an optimized tour, either immediately or at a specific time in the future. The dispatch decision will be done either manually by the operator or automatically by the system based on the operator's rules .

  • as an account using the customer role, you will not have access to the tour. Only admin, organization manager & driver accounts have access to tours.

  • to track progress of the booking being executed, you can keep track of the status of the booking you created as well as the status of individual stops on the booking. These mechanism is described in more detail in the Receiving updates via Webhooks section

To learn more about the Booking status model, and how it relates to that of a tour, please read: Statusmodel of Bookings

Add your order ID as external ID onto a booking

In order to establish a connection between various properties on your internal order model (e.g. order id) and the resulting booking in MotionTools, you can add said properties as Metadata on the booking, stop, or both.

Stop metadata can be added directly when creating the booking, and can also be updated later. Booking metadata has to be added via a separate call to the Update Booking Metadata endpoint.

Booking vs Stop Metadata

When a booking is dispatched into an optimized tour to be executed by the delivery partner, the booking level metadata is not copied over to the tour metadata. This happens because a tour could be created from multiple different bookings.

Stop level metadata is however will be present in the equivalent stop within the tour, since the booking stops themselves are essentially copied in the tour.

At a practical level this means it’s best to place relevant metadata on both the booking and on its containing stops:

  • Booking level metadata: helps you identify the internal order using the metadata on the MotionTools booking

  • Stop level metadata(on each individual stop of the booking): helps the delivery operator identify which stop in a tour belongs to a particular order from your internal system

Besides the internal order ID, metadata can be used to store within bookings/stops (and more) arbitrary useful values from your original order. Examples could be the customer ID, customer rating, internal price, internal delivery estimate, etc.


Integration prerequisites

In order to integrate with your delivery operator powered by MotionTools, you need to have the following available/configured:

  • Customer account: on the MotionTools Tenant of your delivery operator

  • Customer API token: Once you have access to your account, you need to create an API token.

  • service_id : To be provided by the delivery operator, you need to set it when creating a booking. More about services here. If the delivery operator uses just the “default service”, then service_id can be omitted when creating the booking.


Push an order via API

Sample payload for a pickup, customized to food delivery (expand to view)

{ "booking": { "stops": [ { "type": "pickup", "earliest_arrival_at": null, "latest_arrival_at": null, "place_id": "7d6991cb-d669-40ae-acb7-6a2b964e212b", // add a valid place id for the pickup location "additional_information": "info for pickup, e.g. order id or name", "metadata": { "public_order_id": "value", "external_order_id": "value" }, }, { "type": "dropoff", "earliest_arrival_at": null, "latest_arrival_at": null, "lat": 53.54439, // optional, MotionTools will geocode if n/a "lng": 9.940334, "street": "Große Elbstraße", "number": "123", "zip_code": "20097", "city": "Hamburg", "country": "Deutschland", "location_name": "Große Elbstraße 123", "first_name": "Vorname", "last_name": "Nachname", "phone_number": "+491601234123", "additional_information": "Please don't ring the bell", "metadata": { "public_order_id": "value", "external_order_id": "value" }, } ], "scheduled_at": "now" // if you only push bookings for now, you can leave this out, otherwise you can add a timestamp, please note that only timestamps in the future are allowed and they have to be within the opening hours of the operator. Scheduled_at is typically the time when you want a driver to pickup the order from the restaurant. } }

Set the contact details of the final recipient of the order as Stop Contact Details on the dropoff stop in order to allow drivers/couriers from your delivery partner to contact the final recipient, when they’re processing the booking.


Receiving updates via Webhooks

Setting up webhooks can only be done by admin users.

In order to create a webhook, you need to share the URL that you want to receive webhooks to with either a representative of your delivery partner (as they should have admin access), or with a MotionTools support. You also need to specify which events you want receive and potentially which filters to apply.

General information

  • MotionTools can send status updates via webhooks for almost any event that happens within MotionTools

  • The webhooks are lean and only include the most relevant information, such as the event that happened and the booking id that it is about

  • If you want to get more detailed information, you have to fetch the information from the relevant endpoint after receiving a webhook that requires you to get more details, e.g. the “show booking” endpoint

  • The API docs for webhooks can be found here: https://docs.motiontools.io/#tag/Webhooks

  • To ensure that your webhook consumer endpoint is invoked by authentic requests from MotionTools, we added support for webhook request verification. See: Webhook verification

Relevant webhooks

Booking & Stop status updates

  • Driver assigned + en route to the pickup:

    • webhook to use: hailing_booking_stop.transition

    • mapping via stop id of the pickup stop

    • relevant event: start

    • Stop status after event: approaching

  • Driver arrived at the pickup

    • webhook to use: hailing_booking_stop.transition

    • mapping via stop id of the pickup stop

    • relevant event: arrive

    • Stop status after event: arrived

  • Driver completed the pickup

    • webhook to use: hailing_booking_stop.transition

    • mapping via stop id of the pickup stop

    • relevant event: complete

    • Stop status after the event: done

  • Driver en route to drop-off

    • webhook to use: hailing_booking_stop.transition

    • mapping via stop id of the drop-off stop

    • relevant event: start

    • Stop status after event: approaching

  • Driver arrived at drop-off

    • webhook to use: hailing_booking_stop.transition

    • mapping via stop id of the drop-off stop

    • relevant event: arrive

    • Stop status after event: arrived

  • Driver completed to drop-off (should be equal to booking completed, so you can either implement it like this or use the next option with “booking completed”)

    • webhook to use: hailing_booking_stop.transition

    • mapping via stop id of the drop-off stop

    • relevant event: complete

    • Stop status after event: done

  • Booking completed

    • webhook to use: booking.transition

    • mapping via booking id

    • relevant events: complete_stops or force_end

    • Booking status after event: paid

  • Booking canceled

    • webhook to use: booking.transition

    • mapping via booking id

    • relevant events: cancel

    • booking status after event: cancelled

Driver location updates

Up to date ETAs

Booking cycle

Stops cycle

  • scheduled → stop not started yet

  • approaching → driver is approaching the stop

  • arrived → driver arrived at the stop

  • done → driver completed the stop

  • failed → dispatcher failed the stop (in certain configuration is enabled we also allow driver to fail a stop)

Webhook verification

To verify that an incoming webhook request was not maliciously forged or modified we include a signature as a part of the request headers. The signature must be validated on your side against the public key you obtained from your delivery partner when they configured the webhook.

Ed25519 is used as the encryption algorithm, and the signature is given Base64-encoded in the X-Mtools-Signature request header.

Cloudlfare Worker TypeScript sample for verifying webhook authenticity

💡Note: The sample below is suitable for the Cloudlfare Workers/Deno runtime. If you’re using a different JavaScript runtime such as NodeJs or Bun then the below example will not work out of the box. For example, in NodeJs use Ed25519 rather than NODE-ED25519 to identify the algorithm. Changes might also exist when interacting with the crypto library.

The code below only servers as an example. Please adjust accordingly to match the requirements for your runtime.

// Replace this with your actual public key (Base64-encoded)
const PUBLIC_KEY_BASE64 = 'YOUR_PUBLIC_KEY_HERE';

export default {
async fetch(request: Request): Promise<Response> {

// Read body as ArrayBuffer (Uint8Array required for crypto.subtle)
const body = new Uint8Array(await request.arrayBuffer());

// Get signature from header (Base64-encoded)
const signatureBase64 = request.headers.get('X-Mtools-Signature');
if (!signatureBase64) {
return new Response('Missing signature header', { status: 400 });
}
const signature = Uint8Array.from(atob(signatureBase64), c => c.charCodeAt(0));

// Decode public key
const publicKey = Uint8Array.from(atob(PUBLIC_KEY_BASE64), c => c.charCodeAt(0));

// Import public key for Ed25519 verify
const cryptoKey = await crypto.subtle.importKey(
'raw',
publicKey,
{ name: 'NODE-ED25519', namedCurve: 'NODE-ED25519' }, // For Deno/Cloudflare
false,
['verify']
);

// Verify signature
const isValid = await crypto.subtle.verify(
'NODE-ED25519',
cryptoKey,
signature,
body
);

if (!isValid) {
return new Response('Invalid signature', { status: 401 });
}

// Signature valid, process webhook
try {
await procesWebhook(body)
return new Response('Webhook verified!', { status: 200 });
} catch (e) {
return new Response('Internal server error: error processing webhook.', { status: 500 });
}
}
};

Webhook consumption considerations

To ensure performance stability for our platform, we impose the following limits on webhook consumers:

  • We expect a valid 2xx response code to consider successful the delivery of any particular webhook event.

  • 15seconds timeout: if no 2xx response in 15 seconds we consider the delivery failed.

  • 3 retries: each webhook event is retried 3 times. After 3 attempts, the webhook event is never re-sent again and essentially lost.

  • 250 consecutive failed deliveries to the same URL will block/deactivate the webhook and send an email notification to the tenant owner (in this case, your delivery partner). If this should happen, either you delivery partner or MotionTools support will contact you.

Webhook Filtering

If you receive webhook events for bookings that do not belong to you, then perhaps your delivery partner did not correctly set up the Webhook Filters when they configured the webhook for you.

Please contact your delivery partner to adjust this. Relevant article in our help center: Forward Webhooks to your customers for Booking status updates.


Technical considerations & recommendations

Webhook considerations

Data validation

Our API & payload structures & status models are constantly evolving.

As such, we advise:

  • against strictly validating payload structure → your integration should not crash if new properties appear in a MotionTools payload you consume

  • against strictly validating known keyword values as enums (e.g. statuses, transitions, other keyword values such as now for the booking.scheduled_at property) → your integration should be able to deal bug-free and without crashes with new, unknown statuses, transitions, etc.


Testing your integration

For testing purposes you will get access to your own MotionTools tenant that will closely match the tenant of your delivery partner. This tenant will act as your sandbox environment where you can test, iterate and experiment while developing your integration.

However, bear in mind that on this development sandbox tenant, you will have access to the tenant admin user, but in production you will only have access to a customer user on your delivery partner’s production tenant.

As such, please make sure that on your sandbox environment you also create a customer account and that you use that account’s API key (i.e. key with the customer role) while developing your integration on your sandbox tenant.


Further resources

Did this answer your question?