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.

Written by Support
Updated today

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 operator 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 Customer Portal 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 that represents your order in MotionTools. Learn more with our in-depth articles about Bookings and Packages. If you are unsure about which option to go with, please read this article and additionally discuss this with your contact at your designated operator / 3PL. This document describes integrating bookings, which is the default way when you are requesting orders to be delivered within 60 minutes or less and the transport goes straight from the pickup to the drop-off, without touching a hub or warehouse in between.

You can track the progress of your order by listening to Webhooks that are relevant to your use-case. Complete list Here ↗️. Setting up webhooks cannot be done directly in your customer account, but needs assistance from your contact at your provider.

Scheduling modes: Instant vs Scheduled

When you create a booking you can specify a when the delivery operator is expected to show up at the first stop, which usually is the pickup stop.

Currently two options are available: instant & scheduled bookings:

  • Instant bookings: indicate that the driver should come to the first stop now or as soon as possible

  • Scheduled bookings: indicate that the arrival at the first stop should be sometime in the future, known as the scheduled_at time.

More details here.

Time windows

MotionTools allows you to specify when exactly a driver should arrive on each individual stop of the booking (earliest_arrival_at, latest_arrival_at). Additionally you can specify the expected duration of a stop(custom_expected_duration_seconds). Refer to the Create Booking Endpoint request payload for details, in the booking.stops model.

Booking lifecycle

It’s important to understand the main concepts in MotionTools that are involved in the booking lifecycle: Booking, Stop, Tour.

Let's go through the flow and the relevant webhook events to listen to:

  • Create a booking using in the customer role with your preferred scheduling mode (booking.created)

  • Once a booking was dispatched into a tour and a driver started that tour, we trigger the booking.in_progress event

  • Once the driver heads to a specific stops, e.g. pickup stop or drop-off stop, there are events for arriving (booking.stop_arrived) and completing (booking.stop_completed) such stops

  • Once all stops of a booking are completed, the booking itself will be operationally completed as well (part of booking.transition)

To learn more about the Booking status model please read: Status model of Bookings

The relevant webhooks & their payload are described in more detail in the Receiving updates via Webhooks section.

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, please add your own ID onto the booking as external_id. By doing this, your order id can be easily used to find the corresponding booking in MotionTools and you will also get this as part of the updates we sent in case you subscribe to webhooks. That way you can easily link back updates to your transaction/order in your system


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 : You can optionally set it when creating a booking in case the operator offers multiple delivery services and you want to book a specific one (e.g. express vs. same day). More about services here.


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

Location tracking & ETAs

Driver location updates

Up to date ETAs

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?