# Introduction

Welcome to the Sendstack API Guide

CTRL by Sendstack helps you and your team effortlessly manage shipments across multiple partners, automate delivery assignments and track progress. All functionality are available via the CTRL dashboard or via APIs.&#x20;

The Sendstack API is a RESTful web service for developers and teams to programmatically interact with their delivery data, partners, delivery management, and analytics functionality. &#x20;


# The Basics

This section will have most of the information you need to get started with the APIs. Please review them thoroughly before you proceed.&#x20;


# Set up your account

To use the APIs, you need a CTRL by Sendstack account. Follow these steps:&#x20;

* Create a production account [here](https://control.sendstack.africa) (or a sandbox account [here](https://sandbox-control.sendstack.africa/))
* Create a CTRL business&#x20;
* Manage your settings and add a subscription depending on your needs
* Navigate to the APIs section to retrieve your App ID and App Secret
* Update your webhook URLs (if needed)


# Base URLs

Access the live APIs via: <https://api.sendstack.africa/api/v1>

Access the sandbox APIs via: <https://sandbox.sendstack.africa/api/v1>

{% hint style="info" %}
You can't access the Sandbox API with your production keys. You need to create a separate Sandbox account [here](https://sandbox-control.sendstack.africa/)
{% endhint %}


# Authentication

All requests to your CTRL business must be routed to the correct app and authenticated with an App ID and App Secret.&#x20;

The App ID, App Secret, and source must be provided with each request header as defined below:&#x20;

## Sample authentication

<mark style="color:blue;">`GET`</mark> `{baseUrl}/sample`

#### Headers

| Name                                                 | Type   | Description                      |
| ---------------------------------------------------- | ------ | -------------------------------- |
| app\_id<mark style="color:red;">\*</mark>            | String | 1234567                          |
| app\_secret<mark style="color:red;">\*</mark>        | String | MQ9VA2UMSZVW1QET7WLYFLEETFEGVA11 |
| x-sendstack-source<mark style="color:red;">\*</mark> | String | control.sendstack                |

{% tabs %}
{% tab title="200: OK Authenticated" %}

{% endtab %}

{% tab title="401: Unauthorized Unauthorized" %}

{% endtab %}
{% endtabs %}


# Postman

If you use Postman, here is a pre-built a ["collection"](https://documenter.getpostman.com/view/20686469/2s9YyvAzjy) which includes predefined requests for all of our API endpoints.

View our collection [here](https://documenter.getpostman.com/view/20686469/2s9YyvAzjy)


# Responses and Errors

Sendstack's API is RESTful and as such, uses conventional HTTP response codes to indicate the success or failure of requests.

NOTE: Fields marked with an asterisk (<mark style="color:red;">\*</mark>) in the request parameter are required.

| HTTP Status Code | Description                                                                                                                                              |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 200              | Request was successful and intended action was carried out.                                                                                              |
| 201              | A resource has successfully been created.                                                                                                                |
| 400              | A validation or client side error occurred and the request was not fulfilled.                                                                            |
| 401              | The request was not authorized. This can be triggered by passing an app\_id or app\_secret in the request headers or the lack of one.                    |
| 404              | Request could not be fulfilled as the request resource does not exist.                                                                                   |
| 5xx              | Request could not be fulfilled due to an error on Sendstack's end. This shouldn't happen so please report as soon as you encounter any instance of this. |

Sample success response

```json
// Successful Reponse - 200 OK
{
    "status": true,
    "message": "Partners fetched successfully",
    "data": {
        "partners": [
            {
                "type": "deliveryCompany"
            }
        ],
        "page": 1,
        "totalPages": 1,
        "totalCount": 1
    }
}

```

Sample Error Response

```json
// Error Reponse - 401 unauthorized
{
    "status": false,
    "message": "Invalid app_id or app_secret"
}

```


# Webhook Verification

Here is a guide on how to verify Sendstack webhooks:&#x20;

1. **Obtain the signature**: When you receive a webhook event, it includes a signature in the `x-sendstack-signature` header. This signature is generated by computing an HMAC with the SHA256 hash function, using your App Secret as the key, and the `webhookId` on the payload object.
2. **Compute your own signature**: To verify the authenticity of the webhook event, you need to compute your own signature using the same method as above. You can do this by computing an HMAC with the SHA256 hash function, using your App secret as the key, and the `webhookId` property on the payload as the message.
3. **Compare signatures**: Once you have computed your own signature, you can compare it to the signature provided in the `x-sendstack-signature` header. If the two signatures match, then you can be sure that the webhook event was sent by Sendstack and has not been tampered with.

You can use a similar function as displayed below:&#x20;

```javascript
const crypto = require('crypto');

function verifySignature(payload, signature, appSecret) {
  const computedSignature = crypto
    .createHmac("sha256", appSecret)
    .update(JSON.stringify(payload.webhookId))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(computedSignature)
  );
}

```


# Pagination

All GET request endpoints which return a list of items (e.g list of deliveries, list of partners) will be paginated with the following default fields:&#x20;

limit: 15&#x20;

page: 1

You can add these fields as query parameters to change the settings&#x20;


# Rate Limiting

**Overview**

Sendstack API endpoint implements rate limits specifically for failed requests. These limits are designed to enhance security measures and ensure system stability.

**Rate Limit Configuration**

The rate limit for failed authentication requests is configured as follows:

* **Window Period:** 15 minutes
  * Description: The time frame during which the rate limit is applied.
* **Maximum Failed Requests:** 20
  * Description: The maximum number of failed requests allowed within the specified window period.

Exceeding 20 failed requests within a 15-minute window will result in an HTTP 429 (Too Many Requests) response.


# Session

## Generate Session

To establish a websocket connection, you must first generate a **session ID**. This session ID is required for authenticating your websocket connection.

**Request**

* **Endpoint**: `POST {baseUrl}/auth/session`
* **Headers**:\
  Provide your `appId` and `appSecret` in the request headers.

**Response**

* The **session ID** is included in the response body as shown below.
* **Session Expiry**:\
  The generated session ID is valid for **6 hours**. After expiry, you will need to generate a new session.
* To connect to the websocket, include the session ID as a request query param. Detailed instructions on how to establish a websocket connection can be found [here](/live-tracking-apis/positions/stream-positions)

```json
{
    "status": true,
    "message": "Session successfully generated",
    "data": {
        "session": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiYXBwSWQiOiI2NjQyMDk3IiwiYXBwU2VjcmV0IjoiQUNVTEdFV1VaVlE5UlI4NVJBVktLMlRBTEFSOVlSRFEifSwiaWF0IjoxNzQwNDY1NjAzLCJleHAiOjE3NDA0ODcyMDN9.1pEoVBbXvgeDEZHhSjSUFUfN7GEe-EhDXIxUjy7A6oI",
        "expiresAt": "2025-02-25T12:40:03.965Z"
    }
}
```


# Guides

You'll want to know these to understand how to use Sendstack APIs for your business case


# Integrated Partners

We have a growing list of partners that are integrated to the Sendstack CTRL Platform. You can book and track deliveries with any of these partners directly from the CTRL dashboard or APIs

See a list of partners on your CTRL Dashboard&#x20;


# Manual Partners

If your partner isn't technology-enabled or isn't currently integrated with Sendstack, you can add them manually via the Control dashboard.&#x20;

They will get email instructions on how they can access the Sendstack partner's app - where they can update delivery statuses and share information on each delivery.&#x20;

This feature is only available for customers on a subscription plan.&#x20;


# Connected Partners

If you have an existing account with any of our integrated partners, you can connect your API Keys or Credentials via the Control Dashboard when adding a partner. This way, you can book deliveries from the Control dashboard and it'll flow to your partner account.&#x20;

This feature is only available for customers on a subscription plan.&#x20;


# Connected Platforms

You can auto-import deliveries from your Shopify store, Woocommerce store, or via Google sheets.&#x20;

To access this feature, kindly email [info@sendstack.africa](mailto:undefined)


# Payments

How to pay for deliveries

All deliveries booked to integrated partners must be paid for before they are assigned to an integrated partner.&#x20;

You can make payment via either your Sendstack wallet or for each individual batch.&#x20;

For wallet payments, your wallet must have a balance above the total cost of deliveries requested. Otherwise, a payment link will be returned when the delivery is booked.&#x20;

Payments for deliveries to Connected Partners or Manual Partners are handled outside the CTRL Platform. You need to pay for a CTRL Subscription via the Control dashboard.&#x20;


# Addresses and Locations

Every delivery requires an address field and any of the 3 below options depending on the delivery type:&#x20;

Location Code: This maps to an area, local government, or province connected to the address. See how to get list of locations and locationCode [here](/delivery-api/locations/get-delivery-locations)

Latitude and Longitude: This is the geo-coordinate of the address&#x20;

Country Code and City: This is the country code and city of the address. See how to get list of countries and countryCode [here](/delivery-api/locations/get-countries)

The table below describes which fields are permitted based on delivery type

| Delivery Type | Lat/ Long | Location code | Country/ City |
| ------------- | --------- | ------------- | ------------- |
| Intra-city    | Yes       | Yes           | No            |
| Nationwide    | Yes       | Yes           | Yes           |
| International | No        | No            | Yes           |


# Delivery Statuses

Here's a list of available delivery statuses

<table><thead><tr><th width="258">Status</th><th>Meaning</th></tr></thead><tbody><tr><td>PENDING</td><td>Delivery has been booked</td></tr><tr><td>ASSIGNED</td><td>Delivery has been assigned to a partner</td></tr><tr><td>ACCEPTED</td><td>Partner has acknowledged and accepted the job</td></tr><tr><td>REJECTED</td><td>Partner has acknowledged and rejected the job</td></tr><tr><td>PICKED_UP</td><td>Partner has picked up the package</td></tr><tr><td>IN_TRANSIT</td><td>Package is in transit to the destination </td></tr><tr><td>ARRIVED_FOR_DELIVERY</td><td>Partner has arrived at the destination </td></tr><tr><td>DELIVERED</td><td>Partner has delivered the package to the recipient </td></tr><tr><td>UNABLE_TO_DELIVER</td><td>Partner couldn't deliver the package </td></tr><tr><td>ARRIVED_FOR_PICKUP</td><td>Partner has arrived at pickup location</td></tr><tr><td>CANCELLED</td><td>Booking has been cancelled </td></tr></tbody></table>


# Order Tracking

How to track a delivery

All delivery requests will return a batch ID, a tracking ID, and a tracking URL. Both the batch ID and the tracking ID can be used to track delivery requests.&#x20;

* **Batch ID** - used to track and take action for all deliveries placed together. (E.g if an order has one pick-up and multiple drop-off locations, you can track the status of all the drop-offs using the batch ID). Similarly, you can cancel orders using a batch ID.&#x20;
* **Tracking ID** - used to track and take action for one delivery. (e.g. even if an order has one pick-up and multiple drop-off locations, each recipient only needs to track their respective tracking ID). Similarly, you can cancel single deliveries using a tracking ID.


# Common Terminology

| Word           | Meaning                                                                                                                                                                |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Partner        | These are the people who carry out a delivery request. They can be individual riders or delivery companies you work with.                                              |
| Drop           |                                                                                                                                                                        |
| Dropoff window | This is the period during which a customer’s order is expected to be dropped off. It is dynamic and can be changed by an associate depending on happenings on the road |
| Pickup window  | This is the period during which a customer’s order is expected to be picked up. It is dynamic and can be changed by an associate depending on happenings on the road   |


# Packaging

For each booking, you need to enter the height, width, length, and weight of the item's packaging.&#x20;

Alternatively, we've created a list of packaging options you can select via the \[[Packaging](/guides/packaging)]\(/pages/lvPACpjQB47cPaGRxuUc)APIs [here](/delivery-api/packaging).


# Pay on Delivery

Describes the pay on delivery feature available on CTRL

Pay on Delivery (POD) is a CTRL feature aimed at facilitating payment upon receipt of goods. This feature should enable a seamless process for users who opt for payment upon delivery, providing coordination between users, riders, and receivers to streamline transactions securely.

To utilize our POD feature, please proceed with the following steps.

#### **Booking Stage:**

* [ ] Specify a "podAmount" field on each drop when booking a delivery. This field indicates the cost of each item to be delivered.
* [ ] Information on how to book a POD delivery can be found [here](/delivery-api/bookings/request-delivery#sample-success-data-pay-on-delivery).

#### Delivery Stage:

* [ ] Generate an account number by making a POST request to the /drops/payOnDelivery endpoint with a dropId field in the body.
* [ ] Information on how to generate an account number can be found [here](https://app.gitbook.com/o/AbqJZ15X79KABRN1WQFQ/s/-MjBhHPDe_zW7U-q8_Q1/~/changes/85/api/bookings/generate-pod-account-number).

The bank account details are securely generated via Paystack and sent to the receiver of the goods. The account remains valid for 8 hours.

#### **Payment Confirmation:**

* [ ] The receiver uses the provided bank account details to pay for the goods within the stipulated 8-hour window.
* [ ] Upon successful payment, a confirmation is sent to the user and rider, indicating that the item has been paid for.
* [ ] The transaction is marked as complete within the system.

### **Communcation:**

1. Booking Stage: An email is sent to the receiver informing them a POD booking has been made and outlining the POD process
2. Delivery Stage:&#x20;
   * Rider's App Notification: Rider receives a notification upon assignment indicating that the delivery requires POD.
3. Bank Account Creation:
   * Receiver Confirmation: An SMS and email containing the bank account details for payment are sent to the receiver.
4. Payment Confirmation:
   * Upon successful payment, a confirmation is sent to the user and rider, indicating that the item has been paid for.
5. Time-Sensitive Notifications:
   * Payment Deadline Alert: Notifications are dispatched to the receiver, rider, and user when there is limited time left before the payment deadline.
   * Payment Failure: Notifications are sent to both the user and rider in case of payment failure, with instructions on how to proceed.
   * Expired Bank Account: Alerts are sent to the user and receiver if the bank account expires before payment is made, with instructions for reactivation or alternative payment methods.
6. Transaction Summary:  After completion, an email or SMS summarizing the transaction details is sent to the user, rider, and receiver for record-keeping purposes.


# Roles and Permissions

This section outlines and defines the granular permissions structure for various user roles within CTRL. It aims to establish clear boundaries and access levels to ensure operational efficiency, data security, and effective role-based management.

The logistics operations platform accommodates multiple user roles, each serving specific functions with distinct permissions and limitations. This hierarchical approach to access control ensures that users can perform their tasks while preventing unauthorized access to sensitive data and functionalities.


# Associates

Defines the permissions and limitations of associates

* **Permissions:**
  * **Access to Orders:** Associates have unrestricted access to all orders, enabling them to view, update, and manage order statuses on the CTRL business they are assigned to
  * **Invite Delivery Partners:** Associates can invite delivery partners to collaborate within the platform.
  * **Business Data :** They have access to view data across the entire business, facilitating a comprehensive overview.
* **Limitations:**
  * **Invite Restrictions:** Associates cannot invite additional associates or operators to the platform, maintaining controlled access.


# Operators

Defines permissions and limitations of operators

* **Permissions:**
  * **Assigned Partner Orders:** Operators can access and manage orders specific to their assigned delivery partners, ensuring focused control.
  * **Invitations:** They have no authority to invite other roles
  * **Partner-Specific Data:** Operators can view statistics and data specific to their assigned delivery partners.
* **Limitations:**
  * **Restricted Data Access:** Operators do not have access to business-wide data beyond their assigned partner scope.


# Admins

Defines permissions and limitations of admins

* **Permissions:**
  * **Full Permissions Set:** Admins have all permissions granted to Associates and Operators.
  * **Role Assignment:** They can assign all roles within the platform, managing the organizational structure.
  * **Data Access:** Admins have access to all business data, ensuring oversight and comprehensive control.
  * **Payment Data Access:** They can view payment data without detailed payment information
* **Limitations:**
  * **Transactional Actions:** Admins cannot perform actions like making payments or deleting Associates and Operators


# Super Admins

Defines permissions of super admins

* **Permissions:**
  * **Comprehensive Access:** Super Admins have all permissions of Admins, Associates, and Operators.
  * **Payment Authority:** They can make payments within the platform, facilitating financial operations.
  * **Account Management:** Super Admins have the capability to remove associates and operators.


# Tabular presentation

Here is a tabular representation of all roles available within a CTRL business, along with their associated permissions

| Permissions                                        | Associates | Operators | Admin | Super Admin |
| -------------------------------------------------- | ---------- | --------- | ----- | ----------- |
| Access to all platform orders                      | Yes        | Partial   | Yes   | Yes         |
| Invite additional associates                       | No         | No        | No    | Yes         |
| Invite additional operators                        | Yes        | No        | Yes   | Yes         |
| Invite additional delivery partners                | Yes        | Yes       | Yes   | Yes         |
| Update order statuses                              | Yes        | Yes       | Yes   | Yes         |
| View data across entire business                   | Yes        | No        | Yes   | Yes         |
| Access specific orders of assigned partners        | Yes        | Yes       | Yes   | Yes         |
| View partner-specific data and statistics          | Yes        | Yes       | Yes   | Yes         |
| Assign all roles                                   | No         | No        | Yes   | Yes         |
| Access all business data                           | No         | No        | Yes   | Yes         |
| Access payment data (without detailed information) | No         | No        | Yes   | Yes         |
| Make payments                                      | No         | No        | No    | Yes         |
| Remove Operators and Associates                    | No         | No        | No    | Yes         |


# Websockets

Our websockets provide real-time data updates for your tracking devices, ensuring you stay informed as changes occur.

**How It Works**

1. **Authentication**\
   To initiate a WebSocket connection, a valid **session** must be included in the request query.
   * Learn how to generate a session ID [here](/the-basics/session) (link to detailed instructions).
2. **Websocket Connection**\
   Once connected, the websocket will stream real-time data for your tracking devices. Key details to note:
   * **Data Streaming**: Real-time updates are sent as long as the connection remains active.
   * **Rate Limiting**: A maximum of **2 responses per second** is enforced to ensure optimal performance.
   * **Update Triggers**: Updates are only sent when there is a change in the status or location of at least one of your tracking devices.
3. **Getting Started**\
   For step-by-step instructions on connecting to the websocket, refer to our[ websocket connection guide](/live-tracking-apis/positions/stream-positions) (link to detailed instructions).


# Wallet

Endpoints to manage your wallet


# Get Wallet Balance

## Get Wallet Balance

<mark style="color:blue;">`GET`</mark> `{baseUrl}/wallet/balance`

Get the wallet balance associated with your App ID. Call this API before making delivery requests to avoid unexpected errors.&#x20;

{% tabs %}
{% tab title="200 Balance retrieved." %}

```
{
    "status": true,
    "message": "Balance retrieved",
    "data": {
        "balance": 0
    }
}
```

{% endtab %}
{% endtabs %}

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Balance retrieved",
    "data": {}
}
```


# Get Wallet Transactions

Get wallet transactions

## Get Wallet Transactions

<mark style="color:blue;">`GET`</mark> `{{baseUrl}}/wallet/transactions`

{% tabs %}
{% tab title="200: OK Retrieved transactions" %}

```javascript
{
    "status": true,
    "message": "Transactions retrieved",
    "data": [
        {
            "id": "6166b0be3dd87d46743a7ca2",
            "amount": 1000,
            "description": "For delivery with batchId - ENIB61",
            "type": "DEBIT",
            "balance": 1000,
            "createdAt": "2021-10-13T10:11:10.667Z"
        },
        {
            "id": "61a72922fd4492a136466941",
            "amount": 1000,
            "description": "For delivery with batchId - 1XZG7Q",
            "type": "DEBIT",
            "balance": 0,
            "createdAt": "2021-12-01T07:49:54.946Z"
        }
    ]
}
```

{% endtab %}
{% endtabs %}

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Transactions retrieved",
    "data": []
}
```


# Locations

Endpoints to fetch locations


# Get Mapped Locations

## Get Delivery Locations&#x20;

<mark style="color:blue;">`GET`</mark> `{baseUrl}/locations`

This endpoint allows you to view mapped delivery locations and their location code. \
\
Latitude and longitude or location code are required when requesting deliveries.&#x20;

**Query Parameters**

| Name    | Type   | Description           |
| ------- | ------ | --------------------- |
| country | string | 2-letter country code |

{% tabs %}
{% tab title="200 Locations fetched successfully" %}

```
{
    "status": true,
    "message": "Locations successfully retrieved",
    "data": [
        {
            "state": "Lagos State",
            "locals": [
                {
                    "name": "Ajah",
                    "locationCode": "01"
                }
                {
                    "name": "Ajeromi-Ifelodun",
                    "locationCode": "02"
                },
            ]
        }
    ]
}
```

{% endtab %}
{% endtabs %}

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Locations fetched successfully",
    "data": [
        {
            "state": "Abia",
            "locals": [
                {
                    "name": "Aba North",
                    "locationCode": "IZJJ",
                    "isAvailable": true,
                    "isLockerAvailable": false
                },
                //...truncated for brevity
}

```


# Get Countries

## Get Countries

<mark style="color:blue;">`GET`</mark> `{baseUrl}/countries`

This endpoint allows you to view mapped countries and their country code.

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Countries fetched successfully",
    "data": [
        {
            "_id": "64ffa3196e17045193d73a45",
            "countryCode": "AF",
            "createdAt": "2023-09-11T23:30:33.801Z",
            "name": "Afghanistan",
            "updatedAt": "2023-09-11T23:30:33.801Z"
        },
        //...truncated for brevity
}
```


# Estimates

Endpoint to get prices of deliveries based on location


# Get Delivery Price Estimate

## Get Delivery Price

<mark style="color:green;">`POST`</mark> `{baseUrl}/deliveries/offers`

This endpoint provides a price estimate for deliveries between two locations. You can use one of the following methods to specify locations:

1. **Location Code:**\
   Pass a `locationCode` for both the pickup and drop-off locations. Location codes can be retrieved via the [Get Locations](/delivery-api/locations/get-delivery-locations) endpoint.
2. **Latitude and Longitude:**\
   Provide `lat` and `long` values within the pickup and drop-off objects.

#### Required Details for Price Estimate:

* **orderType:** Set to `PROCESSING`
* **Pick-up Details:** Include all relevant information for the pickup location.
* **Drop-off Details:** Include all relevant information for the drop-off location.

#### Request Body

| Name      | Type   | Description     |
| --------- | ------ | --------------- |
| orderType | string |                 |
| pickup    | object | pickup details  |
| drop      | object | dropoff details |

**pickup object**

| Field        | Description                                                             | Data Type | Constraints |
| ------------ | ----------------------------------------------------------------------- | --------- | ----------- |
| long         | Longitude of specified pickup address                                   | Number    | Optional    |
| lat          | Latitude of specified pickup address                                    | Number    | Optional    |
| locationCode | Maps to an area, local government, or province connected to the address | String    | Optional    |
| city         | Pickup city                                                             | String    | Optional    |
| state        | Pickup state                                                            | String    | Optional    |
| country      | Pickup country                                                          | String    | Optional    |
| address2     | Alternative pickup address                                              | String    | Optional    |
| address      | Pickup address                                                          | String    | Optional    |
| postalCode   | Pickup address postal code                                              | String    | Optional    |
| pickupDate   | The date scheduled for the pickup                                       | String    | Optional    |

**Drop object**

| Field        | Description                                                             | Data Type | Constraints |
| ------------ | ----------------------------------------------------------------------- | --------- | ----------- |
| long         | Longitude of specified drop address                                     | Number    | Optional    |
| lat          | Latitude of specified drop address                                      | Number    | Optional    |
| locationCode | Maps to an area, local government, or province connected to the address | String    | Optional    |
| city         | drop city                                                               | String    | Optional    |
| state        | drop state                                                              | String    | Optional    |
| country      | drop country                                                            | String    | Optional    |
| address2     | Alternative drop address                                                | String    | Optional    |
| address      | drop address                                                            | String    | Optional    |
| postalCode   | drop address postal code                                                | String    | Optional    |
| width        | Width of the package (cm)                                               | Number    | Optional    |
| length       | Length of the package (cm)                                              | Number    | Optional    |
| height       | Height of the package (cm)                                              | Number    | Optional    |
| weight       | Weight of the package (kg)                                              | Number    | Optional    |
| category     | category of item                                                        | String    | Optional    |
| podAmount    | Proof of delivery amount                                                | Number    | Optional    |

{% tabs %}
{% tab title="200 Delivery estimate successfully generated" %}

```
{
    "status": true,
    "message": "Available Delivery Offers Returned",
    "data": [
        {
            "name": "DLVR By Sendstack",
            "price": 3200,
            "id": "657217b5901fd21b282399ca",
            "partnerLogo": "https://sendstack.s3.us-west-2.amazonaws.com/partnerDocuments/65bcd97c1e38ed2e78e78e93/supportingDocument/be48d753-075c-4611-9c0a-52ebe8e2b270.png",
            "deliveryTimeline": {
                "estimatedDropoffWindow": {
                    "start": "2024-04-26T13:00:00.000Z",
                    "end": "2024-04-26T18:00:00.000Z"
                },
                "estimatedPickupWindow": {
                    "start": "2024-04-26T07:00:00.000Z",
                    "end": "2024-04-26T13:00:00.000Z"
                }
            },
            "scope": "intracity_scheduled",
            "createdAt": "2024-04-25T16:40:18+01:00"
        }
    ]
}
```

{% endtab %}

{% tab title="400 pickupDate is required" %}

```
{
    "status": false,
    "message": "\"dropoffCode\" is required",
    "data": {
        "statusCode": 400,
        "message": "\"dropoffCode\" is required",
    }
}
```

{% endtab %}
{% endtabs %}

### Sample Success Data using location code

```json
{
    "orderType": "PROCESSING",
    "pickup": {
        "pickupDate": "2024-04-26", //optional
        "locationCode": "0NUP",
        "city": "Ikeja",
        "state": "Lagos",
        "address": "30, Allen Avenue, Ikeja",
        "address2": "5, Allen Avenue, Ikeja"
    },
    "drop": {
        "locationCode": "OHZ2",
        "city": "Yaba",
        "state": "Lagos",
        "address": "7, Adebiyi Street, Yaba",
        "address2": "10, Adebiyi Street, Yaba",
        "category": "Electronic",
        "width": 5,
        "height": 5,
        "weight": 5,
        "length": 5
    }
}
```

```json
{
    "orderType": "PROCESSING",
    "pickup": {
        "pickupDate": "2024-04-27", //optional
        "locationCode": "0NUP",
        "city": "Ikeja", //optional
        "state": "Lagos", //optional
        "address": "30, Allen Avenue, Ikeja",
        "address2": "5, Allen Avenue, Ikeja"
    },
    "drop": {
        "locationCode": "OHZ2",
        "city": "Yaba", //optional
        "state": "Lagos", //optional
        "address": "7, Adebiyi Street, Yaba",
        "address2": "10, Adebiyi Street, Yaba",
        "category": "Electronic",
        "width": 5,
        "height": 5,
        "weight": 5,
        "length": 5
    }
}
```

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Available Delivery Offers Returned",
    "data": [
        {
            "name": "DLVR By Sendstack",
            "price": 3200,
            "id": "657217b5901fd21b282399ca",
            "partnerLogo": "https://sendstack.s3.us-west-2.amazonaws.com/partnerDocuments/65bcd97c1e38ed2e78e78e93/supportingDocument/be48d753-075c-4611-9c0a-52ebe8e2b270.png",
            "deliveryTimeline": {
                "estimatedDropoffWindow": {
                    "start": "2024-04-26T13:00:00.000Z",
                    "end": "2024-04-26T18:00:00.000Z"
                },
                "estimatedPickupWindow": {
                    "start": "2024-04-26T07:00:00.000Z",
                    "end": "2024-04-26T13:00:00.000Z"
                }
            },
            "scope": "intracity_scheduled",
            "createdAt": "2024-04-25T16:40:18+01:00"
        }
    ]
}
```

### Sample Success Data using lat and long

```
{
    "orderType": "PROCESSING",
    "pickup": {
        "pickupDate": "2024-10-19",
        "lat": 6.60398,
        "long": 3.351345,
        "city": "Ikeja",
        "state": "Lagos",
        "address": "30, Allen Avenue, Ikeja"
    },
    "drop": {
        "lat": 6.514299,
        "long": 3.368903,
        "city": "Yaba",
        "state": "Lagos",
        "address": "7, Adebiyi Street, Yaba",
        "width": 5,
        "height": 5,
        "weight": 5,
        "length": 5
    }
}
```

### Sample Sucess response

```
{
  "status": true,
  "message": "Available Delivery Offers Returned",
  "data": [
    {
      "name": "test",
      "offerName": "test",
      "price": 0,
      "id": "665064a7637d793b04a2f5a7",
      "deliveryTimeline": {
        "estimatedPickupWindow": {
          "start": "2024-10-19T06:00:00.000Z",
          "end": "2024-10-19T10:00:00.000Z"
        },
        "estimatedDropoffWindow": {
          "start": "2024-10-19T10:00:00.000Z",
          "end": "2024-10-19T18:00:00.000Z"
        }
      },
      "pickupDate": "2024-10-19",
      "scope": "intracity_scheduled",
      "isApiPartner": false,
      "serviceCharge": 200,
      "createdAt": "2024-10-18T15:32:45+00:00"
    },
    {
      "name": "DLVR By Sendstack",
      "offerName": "DLVR By Sendstack",
      "price": 2500,
      "id": "657217b5901fd21b282399ca",
      "deliveryTimeline": {
        "estimatedPickupWindow": {
          "start": "2024-10-19T06:00:00.000Z",
          "end": "2024-10-19T10:00:00.000Z"
        },
        "estimatedDropoffWindow": {
          "start": "2024-10-19T10:00:00.000Z",
          "end": "2024-10-19T18:00:00.000Z"
        }
      },
      "pickupDate": "2024-10-19",
      "scope": "intracity_scheduled",
      "isApiPartner": false,
      "longDistance": false,
      "serviceCharge": 200,
      "createdAt": "2024-10-18T15:32:45+00:00"
    },
    {
      "name": "Messenger",
      "offerName": "Messenger",
      "price": 2700,
      "id": "6568795e0197e613544aaaca",
      "deliveryTimeline": {
        "estimatedDropoffWindow": {
          "start": "2024-10-19T10:00:00.000Z",
          "end": null
        },
        "estimatedPickupWindow": {
          "start": "2024-10-19T06:00:00.000Z",
          "end": null
        }
      },
      "express": true,
      "pickupDate": "2024-10-19",
      "scope": "intracity_scheduled",
      "isApiPartner": true,
      "serviceCharge": 200,
      "createdAt": "2024-10-18T15:32:45+00:00"
    }
  ]
}
```

### Sample Error Data

```json
{
    "orderType": "PROCESSING",
    "pickup": {
        "pickupDate": "2024-04-26",
        "city": "Ikeja",
        "state": "Lagos",
        "address": "30, Allen Avenue, Ikeja",
        "address2": "5, Allen Avenue, Ikeja"
    },
    "drop": {
        "city": "Yaba",
        "state": "Lagos",
        "address": "7, Adebiyi Street, Yaba",
        "address2": "10, Adebiyi Street, Yaba",
        "category": "Electronic",
        "width": 5,
        "height": 5,
        "weight": 5,
        "length": 5
    }
}
```

```json
// Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Pickups in Lagos or Intra-city deliveries must include lat/long or locationCode"
}
```


# Partners


# Get Partners

## Get partners

<mark style="color:blue;">`GET`</mark> `{baseUrl}/partners`

Get list of partners avaiable for your business

{% tabs %}
{% tab title="200: OK Partner fetched successfully" %}

{% endtab %}
{% endtabs %}

```json
//Sample Response Body - 200 OK
{
    "status": true,
    "message": "Partners fetched successfully",
    "data": {
        "partners": [
        //...truncated for brevity
        ]
}
```


# Get Partner

## Get individual partner

<mark style="color:blue;">`GET`</mark> `{baseUrl}/partner/:partnerId`

Get individual partner details

#### Path Parameters

| Name                                        | Type   | Description |
| ------------------------------------------- | ------ | ----------- |
| partnerId<mark style="color:red;">\*</mark> | String |             |

{% tabs %}
{% tab title="200: OK " %}

{% endtab %}
{% endtabs %}

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "",
    "data": {
        "bankAccountDetails": {
            "accountName": "test",
            "accountNumber": "0001112223"
        },
        //...truncated for brevity
    }
}

```


# Update Partner Profile

## Update partner profile

<mark style="color:purple;">`PATCH`</mark> `{baseUrl}/partners/:id`

Update partner profile by id

#### Path Parameters

| Name                                 | Type   | Description |
| ------------------------------------ | ------ | ----------- |
| id<mark style="color:red;">\*</mark> | String |             |

#### Request Body

| Name        | Type   | Description |
| ----------- | ------ | ----------- |
| name        | String |             |
| phone       | String |             |
| disabledSms | String |             |

{% tabs %}
{% tab title="200: OK Partner updated successfully" %}

{% endtab %}
{% endtabs %}

### Sample Success Data

```json
// Sample Request Body
{
    "name": "test",
    "phone": "09011233344",
    "disabledSms": false
}
```

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Partner updated successfully",
    "data": {
        "bankAccountDetails": {
            "accountName": "test",
            "accountNumber": "0001112223"
        },
        //...truncated for brevity
    }
}

```


# Add Partner Off Days

## Add partner off days

<mark style="color:green;">`POST`</mark> `{baseUrl}/partners/offDays`

Add partner off day(s)

#### Request Body

| Name                                        | Type           | Description |
| ------------------------------------------- | -------------- | ----------- |
| partnerId<mark style="color:red;">\*</mark> | String         |             |
| dates<mark style="color:red;">\*</mark>     | Array\<string> |             |

{% tabs %}
{% tab title="200: OK Off days added successfully" %}

{% endtab %}

{% tab title="400: Bad Request Could not add off day: Partner not found" %}

{% endtab %}
{% endtabs %}

### Sample Success Data

```json
// Sample Request Body
{
    "partnerId": "{{partnerId}}",
    "dates":["2024-05-20"]
}
```

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Off days added successfully",
    "data": [
        "2024-05-20T00:00:00.000Z"
    ]
}
```

### Sample Error Data

```json
// Sample Request Body
{
    "dates":["2024-05-20"]
}
```

```json
// Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Could not add off day: Partner not found"
}
```


# Delete Partner Off Day

## Delete partner off days

<mark style="color:green;">`POST`</mark> `{baseUrl}/partners/offDays/delete`

Delete partner off day(s)

#### Request Body

| Name                                        | Type           | Description |
| ------------------------------------------- | -------------- | ----------- |
| partnerId<mark style="color:red;">\*</mark> | String         |             |
| dates<mark style="color:red;">\*</mark>     | Array\<string> |             |

{% tabs %}
{% tab title="200: OK Off day deleted successfully" %}

{% endtab %}

{% tab title="400: Bad Request Could not remove off day: Partner not found" %}

{% endtab %}
{% endtabs %}

### Sample Success Data

```json
//Sample Request Body
{
    "partnerId": "{{partnerId}}",
    "date":["2024-05-20"]
}
```

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Off day deleted successfully",
    "data": []
}
```

### Sample Error Data

```json
// Sample Request Body
{
    "partnerId": "invalid id",
    "date":["2024-05-20"]
}

```

```json
// Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Could not delete off day: Partner not found"
}
```


# Packaging

Sendstack has a curated list of packaging sizing which provides information on the sizes of items we can deliver.

You can see all available sizing using the request format below.

## Get packaging

<mark style="color:blue;">`GET`</mark> `{baseUrl}/deliveries/packagings`

Get all packaging options

{% tabs %}
{% tab title="200: OK Packaging options successfully retrieved" %}

{% endtab %}
{% endtabs %}

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Packaging options successfully retrieved",
    "data": [
        {
            "_id": "64f8eafb0ad821281e017fb1",
            "__v": 0,
            "createdAt": "2023-09-06T21:11:23.342Z",
            "height": 12,
            "length": 12,
            "name": "Sendstack Standard",
            "updatedAt": "2023-09-06T21:11:23.342Z",
            "width": 33
        },
        //...truncated for brevity
    ]
}
```


# Categories

View all categories of items we can deliver using the request format below.

## Get categories

<mark style="color:blue;">`GET`</mark> `{baseUrl}/deliveries/categories`

Get all categories

{% tabs %}
{% tab title="200: OK Packaging options successfully retrieved" %}

{% endtab %}
{% endtabs %}

```json
// Some code
```


# Bookings


# New Booking

## Request delivery

<mark style="color:green;">`POST`</mark> `{baseUrl}/deliveries`

To create a new delivery booking, provide the following details:

1. **Pick-up Details**\
   Include the necessary pick-up information such as address, contact person etc.
2. **Drop-off Details**\
   Include drop-off information such as address, contact person etc.
3. **Optional Customer Contact**\
   You can specify a customer by including `bookingName` and either:

   * **bookingEmail**: The customer's email address.

   * **bookingPhone**: The customer's phone number.

   > **Note**: If neither field is provided, the delivery will not be associated with a customer.
   >
   > **`bookingName`** is optional but can be included to specify the customer's name.
4. **Order Type**\
   Set the `orderType` to `PROCESSING`. This ensures that the delivery will be handled by your partners.
5. **Optional Partner Assignment**\
   You can assign the delivery to a specific partner at the time of booking by using the `assignedPartner` field on the drop object. Provide the partner's ID, which can be obtained from the [**Get Delivery Price Estimate**](/delivery-api/estimates/get-delivery-price) request.
6. **Optional custom fields**

   You can include any custom fields configured via the dashboard directly within the `pickup` and `drop` objects.

   These fields are useful for capturing additional, business-specific information during booking.

   For example:\
   If you've added custom fields in the dashboard like:

   * `pickup_house_number` on the pickup form
   * `dropoff_house_number` on the dropoff form

   You can include them in your booking via the `pickup` and `drop` objects respectively as shown on the sample payload below.

#### Headers

<table><thead><tr><th width="284">Name</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>app_id<mark style="color:red;">*</mark></td><td>String</td><td></td></tr><tr><td>app_secret<mark style="color:red;">*</mark></td><td>String</td><td></td></tr></tbody></table>

#### Request Body

| Name                                        | Type   | Description                        |
| ------------------------------------------- | ------ | ---------------------------------- |
| pickup<mark style="color:red;">\*</mark>    | object | Pickup details                     |
| drops<mark style="color:red;">\*</mark>     | array  | Dropoff locations and details      |
| orderType<mark style="color:red;">\*</mark> | string |                                    |
| bookingName                                 | string | specifies the name of the customer |
| bookingPhone                                | string | specifies phone number of customer |
| bookingEmail                                | string | specifies email of customer        |

**pickup object**

| Field           | Description                                                                                              | Data Type | Constraints |
| --------------- | -------------------------------------------------------------------------------------------------------- | --------- | ----------- |
| long            | Longitude of specified pickup address                                                                    | Number    | Optional    |
| lat             | Latitude of specified pickup address                                                                     | Number    | Optional    |
| locationCode    | Maps to an area, local government, or province connected to the address                                  | String    | Optional    |
| city            | Pickup city                                                                                              | String    | Optional    |
| state           | Pickup state                                                                                             | String    | Optional    |
| country         | Pickup country                                                                                           | String    | Optional    |
| address2        | Address line 2                                                                                           | String    | Optional    |
| address         | Pickup address                                                                                           | String    | Required    |
| pickupName      | Pickup name                                                                                              | String    | Required    |
| pickupEmail     | Pickup email                                                                                             | String    | Optional    |
| pickupNumber    | Pickup phone number                                                                                      | String    | Required    |
| altPickupNumber | Alternative pickup phone number                                                                          | String    | Optional    |
| postalCode      | Pickup address postal code                                                                               | String    | Optional    |
| note            | Pickup note                                                                                              | String    | Optional    |
| pickupDate      | The date scheduled for the pickup                                                                        | String    | Optional    |
| pickupOtp       | Preferred pickup otp to be used. If not specified, a delivery pickup otp will be automatically generated | String    | Optional    |

#### Drop Object

| Field                  | Description                                                                                                                            | Data Type | Constraints           |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------- |
| long                   | Longitude of the drop-off address                                                                                                      | Number    | Optional              |
| lat                    | Latitude of the drop-off address                                                                                                       | Number    | Optional              |
| locationCode           | Maps to a specific area or region connected to the address                                                                             | String    | Optional              |
| city                   | Drop-off city                                                                                                                          | String    | Optional              |
| state                  | Drop-off state                                                                                                                         | String    | Optional              |
| country                | Drop-off country                                                                                                                       | String    | Default: "NG"         |
| address                | Drop-off address                                                                                                                       | String    | Required              |
| address2               | Address line 2                                                                                                                         | String    | Optional              |
| recipientName          | Recipient’s name                                                                                                                       | String    | Required              |
| recipientNumber        | Recipient’s phone number                                                                                                               | String    | Required              |
| recipientEmail         | Recipient’s email address                                                                                                              | String    | Optional              |
| altRecipientNumber     | Alternative recipient phone number                                                                                                     | String    | Optional              |
| express                | Indicates if express delivery is requested                                                                                             | Boolean   | Optional              |
| category               | Delivery category                                                                                                                      | String    | Optional              |
| note                   | Additional delivery notes                                                                                                              | String    | Optional              |
| value                  | Value of the item being delivered                                                                                                      | Number    | Optional              |
| description            | Description of the item                                                                                                                | String    | Optional              |
| height                 | Height of the package (cm)                                                                                                             | Number    | Optional              |
| length                 | Length of the package (cm)                                                                                                             | Number    | Optional              |
| width                  | Width of the package (cm)                                                                                                              | Number    | Optional              |
| weight                 | Weight of the package (kg)                                                                                                             | Number    | Optional              |
| assignedPartner        | ID of the assigned partner                                                                                                             | String    | Optional              |
| postalCode             | Drop-off address postal code                                                                                                           | String    | Optional              |
| categoryId             | ID of the delivery category                                                                                                            | String    | Optional              |
| podAmount              | Pay on delivery amount                                                                                                                 | Number    | Optional              |
| amount                 | Delivery amount. If provided, it will override the default calculated amount for the delivery                                          | Number    | Optional              |
| partnerFee             | Fee charged by the partner                                                                                                             | Number    | Optional              |
| estimatedPickupWindow  | <p>Estimated pickup time window (date and time range).<br>If provided, it will override the default pickup window for the delivery</p> | Object    | Optional (start, end) |
| estimatedDropoffWindow | Estimated drop-off time window (date and time range). If provided, it will override the default drop-off window for the delivery       | Object    | Optional (start, end) |
| externalOrderId        | External order id                                                                                                                      | String    | Optional              |
| dropoffOtp             | Preferred dropoff otp to be used. If not specified, a delivery dropoff otp will be automatically generated                             | String    | Optional              |

{% tabs %}
{% tab title="200 Request successfully placed" %}

```
{
      id: '65fbe637eadabd3da02c3143',
      trackingUrl: 'https://app.sendstack.africa/tracking?batchId=R8XB5B',
      paymentStatus: 'PAID',
      paymentSource: 'self',
      batchId: 'R8XB5B',
      totalAmount: 1500,
      initiator: '65fbe629eadabd3da02c2fab',
      pickup: {
        address: '1894 Walker Motorway',
        pickupName: 'Ramona Rowe',
        pickupNumber: '+2349092264624',
        pickupDate: '2024-03-22T00:00:00.000Z',
        lga: 'Location 1',
        state: 'Lagos'
      },
      drops: [
        {
          id: '65fbe637eadabd3da02c3141',
          fulfillmentPartner: 'Sendstack',
          podPaymentStatus: 'PENDING',
          status: 'ASSIGNED',
          trackingId: 'S1ZZ4U',
          trackingUrl: 'https://app.sendstack.africa/tracking?trackingId=S1ZZ4U',
          batchId: 'R8XB5B',
          address: '2501 Smitham Junctions',
          recipientName: 'Melinda King',
          recipientNumber: '+2348099793911',
          //...truncated for brevity
        }
      ]
    }

```

{% endtab %}

{% tab title="400: Bad Request {{field}} is required" %}

```
{
    "status": false,
    "message": "Invalid app_id or app_secret",
    "data": {
        "statusCode": 401,
        "message": "Invalid app_id or app_secret",
        "isOperational": true,
    }
}
```

{% endtab %}

{% tab title="422: Unprocessable Entity Low wallet balance" %}

```javascript
{
    // Response
}
```

{% endtab %}
{% endtabs %}

```
// Sample Request information
Url: https://sandbox.sendstack.africa/api/v1/deliveries
method: POST
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

### Sample Success Data

```json
//Sample Request Body
{  
   "orderType": "PROCESSING",
   "bookingEmail": "test@gmail.com",
   "bookingName": "test",
    "pickup": {
      "address": "928 Lowe Lakes",
      "pickupName": "Miss Rudolph Dietrich",
      "pickupNumber": "+2348015793998",
      "altPickupNumber": "+2348015020200",
      "pickupDate": "2024-12-04",
      "lat": 6.602928,
      "long": 3.345006,
      "pickup_house_number": 1 //custom field
    },
    "drops": [
      {
        "address": "419 Graham View",
        "recipientName": "Robyn Nitzsche DVM",
        "recipientNumber": "+2348007384794",
        "altRecipientNumber": "+2348002722212",
        "lat": 6.602928,
        "long": 3.345006,
        "estimatedPickupWindow": {
                "start": "2024-12-04T06:00:00.00Z",
                "end": "2024-12-04T12:00:00.00Z"
          },
          "estimatedDropoffWindow": {
                "start": "2024-12-04T12:00:00.00Z",
                "end": "2024-12-04T17:00:00.00Z"
          },
          "dropoff_house_number": 2 //custom field
        }
    ]
}
```

### Sample Success Data(Pay on Delivery)

```json

//Sample Request Body

{  
   "orderType": "PROCESSING",
    "pickup": {
      "address": "928 Lowe Lakes",
      "pickupName": "Miss Rudolph Dietrich",
      "pickupNumber": "+2348015793998",
      "pickupDate": "2024-03-22",
      "locationCode": "JJ2M",
    },
    "drops": [
      {
        "address": "419 Graham View",
        "podAmount": 1000,
        "recipientName": "Robyn Nitzsche DVM",
        "recipientNumber": "+2348007384794",
        "locationCode": "XV4C",
      }
    ]
}
```

### Sample Error Data

```json
// Sample Request Body
{  
   "orderType": "PROCESSING",
    "pickup": {
      "pickupName": "Miss Rudolph Dietrich",
      "pickupNumber": "+2348015793998",
      "pickupDate": "2024-03-22",
      "locationCode": "JJ2M",
    },
    "drops": [
      {
        "recipientName": "Robyn Nitzsche DVM",
        "recipientNumber": "+2348007384794",
        "locationCode": "XV4C",
      }
    ]
}
```

```json
// Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "\"address\" is required, \"address\" is required"
}

```

A 400 Bad Request is returned for any missing required field.


# Generate POD account number

Details on how to generate an account number for a POD delivery

## Generate account number for POD delivery

<mark style="color:green;">`POST`</mark> `{baseUrl}/drops/cancel`

The bank account details are securely generated via Paystack and sent to the receiver of the goods. The account remains valid for 8 hours.

#### Headers

| Name                                          | Type   | Description         |
| --------------------------------------------- | ------ | ------------------- |
| app\_id<mark style="color:red;">\*</mark>     | string | Your app ID         |
| app\_secret<mark style="color:red;">\*</mark> | string | Your app secret key |

#### Request Body

| Name                                     | Type   | Description |
| ---------------------------------------- | ------ | ----------- |
| dropId<mark style="color:red;">\*</mark> | String | dropId      |

{% tabs %}
{% tab title="200 Deliveries cancelled" %}

```
{
  "status": true,
  "message": "Delivery cancelled",
  "data": {
  }
}
```

{% endtab %}

{% tab title="400: Bad Request Invalid dropId" %}

```
{
    "status": false,
    "message": "Invalid app_id or app_secret",
    "data": {
        "statusCode": 401,
        "message": "Invalid app_id or app_secret",
        "isOperational": true,
    }
}
```

{% endtab %}
{% endtabs %}

```
// Sample Request information
Url: https://sandbox.sendstack.africa/drops/payOnDelivery
Method: POST
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
Body:
  -dropId
```

### Sample Success Data

```json
// Sample Request body
{
    "dropId": "yourDropId"
}
```

```json
//Sample Response Body - 200 OK

```

### Sample Error Data

```json
{
    "dropId": "invalid dropId"
}
```

```json
//Sample Response Body - 400 OK
{
    "status": false,
    "message": "Invalid dropId"
}
```


# Fetch Deliveries

## Fetch Deliveries

<mark style="color:blue;">`GET`</mark> `{baseUrl}/deliveries`

This endpoint retrieves all deliveries booked under your business. You can apply filters using the query parameters outlined below:

* **`batchId`**: Filters deliveries by the specified batch ID.
* **`trackingId`**: Filters deliveries by the specified tracking ID.
* **`customer`**: Filters deliveries associated with a specific customer.

#### Query Parameters

| Name            | Type   | Description                                     |
| --------------- | ------ | ----------------------------------------------- |
| batchId         | string | Batch Id generated for a delivery request       |
| trackingId      | string | Tracking Id generated for a delivery request    |
| externalOrderId | string | External order id                               |
| limit           | number | The result limits for pagination, default is 15 |
| page            | number | page option for pagination, default is 1        |
| customer        | string | customer id                                     |

#### Headers

| Name                                          | Type   | Description |
| --------------------------------------------- | ------ | ----------- |
| app\_id<mark style="color:red;">\*</mark>     | String |             |
| app\_secret<mark style="color:red;">\*</mark> | String |             |

### Sample Success Data

```
// Sample Request information
Url: https://sandbox.sendstack.africa/api/v1/deliveries
Method: GET
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Deliveries successfully retrieved",
    "data": {
        "drops": [
            {
                "id": "65b8c2c734d8cd1be099fd45",
                "deliveryId": "65b8c2c734d8cd1be099fd47",
                "status": "PENDING",
                "paymentStatus": "PENDING",
                "trackingId": "E033LM",
                "batchId": "T4SYZ3",
                "customer": {
                    "id": "670f5170d244e83e2424919e",
                    "name": "Bob",
                    "phone": "+2348151911111"
                },
                "customFields": { 
                    "pickup": {
                        "pickup_house_number": 1
                    },
                    "drop": {
                        "dropoff_house_number": 2
                    }
                 }
           }
           //...trunated for brevity
}
```

### Filter By batchId

```
// Sample Request information
Url: https://sandbox.sendstack.africa/api/v1/deliveries?batchId={{batchId}}
Method: GET
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
Query parameter:
 - batchId = "delivery batchId"
```

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Deliveries successfully retrieved",
    "data": {
        "drops": [
            {
                "id": "65b8c02734d8cd1be099fcf1",
                "status": "PENDING",
                "paymentStatus": "PENDING",
                "trackingId": "KH3I65",
                "batchId": "MFUD2A",
                "address": "95975 Barton Fields",
                "pickupName": "Toni Dicki PhD",
                "pickupNumber": "+2348017126895",
                "altPickupNumber": "+2348033000000",
                "pickupLga": "Yaba - Makoko",
                "pickupState": "Lagos",
                "recipientName": "Kabir",
                "recipientNumber": "+23490234500690",
                "recipientLga": "Yaba - Makoko",
                "amount": 1500,
                "estimatedPickupWindow": {
                    "date": "2024-10-27T08:00:00.000Z",
                    "start": "2024-10-27T08:00:00.000Z",
                    "end": "2024-10-27T13:00:00.000Z"
                },
                //...truncated for brevity
    }
}
```

### Filter By a Specific customer

```
// Sample Request information
Url: https://sandbox.sendstack.africa/api/v1/deliveries?customer={{customerId}}
Method: GET
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
Query parameter:
 - customer = "customerId"
```

```
// Sample success Response Body - 200 OK
{
    "status": true,
    "message": "Deliveries successfully retrieved",
    "data": {
        "drops": [
            {
                "id": "65b8c02734d8cd1be099fcf1",
                "status": "PENDING",
                "paymentStatus": "PENDING",
                "trackingId": "KH3I65",
                "batchId": "MFUD2A",
                "address": "95975 Barton Fields",
                "pickupName": "Toni Dicki PhD",
                "pickupNumber": "+2348017126895",
                "altPickupNumber": "+2348033000000",
                "pickupLga": "Yaba - Makoko",
                "pickupState": "Lagos",
                "recipientName": "Kabir",
                "recipientNumber": "+23490234500690",
                "recipientLga": "Yaba - Makoko",
                "amount": 1500,
                "customer": {
                    "id": "670f5170d244e83e2424919e",
                    "name": "Bob",
                    "phone": "+2348151911111"
                }
                //...truncated for brevity
    }
}
```


# Update Delivery Details

## Update Delivery Details

<mark style="color:orange;">`PUT`</mark> `{baseUrl}/deliveries/drops/:id`

Update delivery details by providing the drop ID

#### Path Parameters

| Name                                 | Type   | Description |
| ------------------------------------ | ------ | ----------- |
| id<mark style="color:red;">\*</mark> | String |             |

#### Headers

| Name                                          | Type   | Description     |
| --------------------------------------------- | ------ | --------------- |
| app\_id<mark style="color:red;">\*</mark>     | String | Your app ID     |
| app\_secret<mark style="color:red;">\*</mark> | String | Your app secret |

#### Request Body

| Name               | Type   | Description |
| ------------------ | ------ | ----------- |
| address            | String |             |
| recipientName      | String |             |
| recipientNumber    | String |             |
| altRecipientNumber | String |             |
| note               | String |             |
| locationCode       | String |             |

{% tabs %}
{% tab title="200: OK Delivery updated successfully" %}

```javascript
{
    // Response
}
```

{% endtab %}

{% tab title="400: Bad Request Invalid dropId" %}

```javascript
{
    // Response
}
```

{% endtab %}
{% endtabs %}

### Sample Success Data

```
// Sample Request information
Url: https://sandbox.sendstack.africa/api/v1/deliveries/drops/yourDropId
NB: Replace yourDropId with an actual dropId
Method: PUT
Headers:
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

```json
//Sample Request Body
{
    "locationCode": "04",
    "address": "4, Toyin Street, Yaba",
    "recipientName": "Clinton",
    "recipientNumber": "09067234756"
}
```

```json
// Sample Response Body - 200 OK
{
    "status": true,
    "message": "Delivery updated successfully",
    "data": {
        "id": "65b8c2c734d8cd1be099fd45",
        "processed": false,
        "status": "PENDING",
        "paymentStatus": "PENDING",
        //..truncated for brevity
    }
}
```

### Sample Error Data

```
// Sample Request information
Url: https://sandbox.sendstack.africa/api/v1/deliveries/drops/invalidDropId
Method: PUT
Headers:
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

```json
// Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Invalid dropId"
}
```


# Update Drop Statuses

## Update Drop Statuses

<mark style="color:green;">`POST`</mark> `{baseUrl}/drops/status`

Update the status of drops. Only available for deliveries booked outside the Sendstack network

#### Request Body

| Name                                      | Type   | Description |
| ----------------------------------------- | ------ | ----------- |
| status<mark style="color:red;">\*</mark>  | String |             |
| dropIds<mark style="color:red;">\*</mark> | Array  |             |


# Cancel Drops

## Cancel Drops

<mark style="color:green;">`POST`</mark> `{baseUrl}/drops/cancel`

Cancel drops by passing an array of dropIDs&#x20;

#### Headers

| Name                                          | Type   | Description         |
| --------------------------------------------- | ------ | ------------------- |
| app\_id<mark style="color:red;">\*</mark>     | string | Your app ID         |
| app\_secret<mark style="color:red;">\*</mark> | string | Your app secret key |

#### Request Body

| Name                                      | Type           | Description       |
| ----------------------------------------- | -------------- | ----------------- |
| dropIds<mark style="color:red;">\*</mark> | Array\<string> | array of drop IDs |
| trackingId                                | String         |                   |
| batchId                                   | String         |                   |
| note                                      | String         |                   |

{% tabs %}
{% tab title="200 Deliveries cancelled" %}

```
{
  "status": true,
  "message": "Delivery cancelled",
  "data": {
  }
}
```

{% endtab %}

{% tab title="400: Bad Request Invalid dropId" %}

```
{
    "status": false,
    "message": "Invalid app_id or app_secret",
    "data": {
        "statusCode": 401,
        "message": "Invalid app_id or app_secret",
        "isOperational": true,
    }
}
```

{% endtab %}
{% endtabs %}

```json
// Sample Request information
Url: https://sandbox.sendstack.africa/api/v1/drops/cancel
Method: POST
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

### Sample Success Data

```json
// Sample Request Body
{
    "dropIds": ["{{dropId}}"],
    "trackingId": "BN8IU3",
    "batchId": "MFUD2A",
    "note": "drop cancelled"
}
```

```json
//Sample Response Body - 200 OK
{
    "status": true,
    "message": "Deliveries cancelled",
    "data": {}
}
```

### Sample Error Data

```json
{
    "dropIds": ["invalid dropId"]
}
```

```json
//Sample Response Body - 400 OK
{
    "status": false,
    "message": "Invalid dropIds"
}
```


# Reschedule Delivery

Reschedule Delivery by delivery ID

## Reschedule Delivery

<mark style="color:green;">`POST`</mark> `{baseUrl}/deliveries/:id/reschedule`

Reschedule Delivery by passing deliveryId

#### Path Parameters

| Name                                 | Type   | Description |
| ------------------------------------ | ------ | ----------- |
| id<mark style="color:red;">\*</mark> | String |             |

#### Headers

| Name                                          | Type   | Description |
| --------------------------------------------- | ------ | ----------- |
| app\_id<mark style="color:red;">\*</mark>     | String |             |
| app\_secret<mark style="color:red;">\*</mark> | String |             |

#### Request Body

| Name                                         | Type   | Description |
| -------------------------------------------- | ------ | ----------- |
| pickupDate<mark style="color:red;">\*</mark> | String |             |

{% tabs %}
{% tab title="200 Rescheduled delivery" %}

```
{
  "status": true,
  "message": "Delivery cancelled",
  "data": {
  }
}
```

{% endtab %}

{% tab title="400: Bad Request Invalid Delivery Id" %}

```
{
    "status": false,
    "message": "Invalid app_id or app_secret",
    "data": {
        "statusCode": 401,
        "message": "Invalid app_id or app_secret",
        "isOperational": true,
    }
}
```

{% endtab %}
{% endtabs %}

### Sample Success Data

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/deliveries/yourDeliveryId/reschedule
NB: Replace yourDeliveryId with an actual delivery Id
Method: POST
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

```json
// Sample Request body
{
    "pickupDate": "2024-09-22"
}
```

```json
// Sample Response Body - 200 OK
{
       "status": true,
       "message": "Rescheduled delivery",
       "data": {
       "pickup": {
          "address": "42230 Gene Unions",
          "pickupName": "Theodore Dietrich IV",
          "pickupNumber": "8032368985",
          "pickupDate": "2024-03-22T00:00:00.000Z",
          "locationCode": "01",
          "country": "NG",
          "lat": 6.602928,
          "long": 3.345006,
          "lga": "Location 1",
          "state": "Lagos",
          //..truncated for brevity
        }
    }
```

### Sample Error Data

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/deliveries/invalidDeliveryId/reschedule
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

```json
//Sample Response Body - 200 OK
{
    "status": false,
    "message": "Invalid deliveryId"
}
```


# Update Delivery Window

Update delivery window by specifying dropId, control business id and updated delivery window.

NB: The 'user' field in the request body is the control business id.

## Update delivery window

<mark style="color:green;">`POST`</mark> `{baseUrl}/drops/updateDeliveryWindow`

#### Request Body

| Name                                                     | Type           | Description |
| -------------------------------------------------------- | -------------- | ----------- |
| estimatedPickupWindow<mark style="color:red;">\*</mark>  | Object         |             |
| estimatedDropoffWindow<mark style="color:red;">\*</mark> | Object         |             |
| user                                                     | String         |             |
| dropIds<mark style="color:red;">\*</mark>                | Array\<String> |             |
| pickupDate                                               | String         |             |

{% tabs %}
{% tab title="200 Delivery window updated" %}

```
{
  "status": true,
  "message": "Delivery cancelled",
  "data": {
  }
}
```

{% endtab %}

{% tab title="400: Bad Request Invalid Delivery Id" %}

```
{
    "status": false,
    "message": "Invalid app_id or app_secret",
    "data": {
        "statusCode": 401,
        "message": "Invalid app_id or app_secret",
        "isOperational": true,
    }
}
```

{% endtab %}
{% endtabs %}

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/drops/updateDeliveryWindow
Method: POST
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

### Sample Success Data

```json
// Sample Request body
{
    "estimatedPickupWindow": {
        "date": "2024-09-22 00:00:00",
        "start": "2024-09-22  07:00:00",
        "end": "2024-09-22 13:00:00"
    },
    "estimatedDropoffWindow": {
        "date": "2024-09-22 00:00:00",
        "start": "2024-09-22 13:00:00",
        "end": "2024-09-22 18:00:00"
    },
    "user": "65b78c4e13d8753d300a1c59",
    "dropIds": ["65b8c2c734d8cd1be099fd45"],
    "pickupDate": "2024-09-22"
}
```

<pre class="language-json"><code class="lang-json"><strong>//Sample Response Body - 200 OK
</strong><strong>{
</strong>    "status": true,
    "message": "Delivery window updated",
    "data": [
        {
            "id": "65b8c2c734d8cd1be099fd45",
            "status": "PENDING",
            "paymentStatus": "PENDING",
            "trackingId": "E033LM",
            //...truncated for brevity
        }
    ]
}
</code></pre>

### Sample Error Data

```json
// Sample Request body
{
    "estimatedPickupWindow": {
        "date": "2024-09-22 00:00:00",
        "start": "2024-09-22  07:00:00",
        "end": "2024-09-22 13:00:00"
    },
    "estimatedDropoffWindow": {
        "date": "2024-09-22 00:00:00",
        "start": "2024-09-22 13:00:00",
        "end": "2024-09-22 18:00:00"
    },
    "user": "65b78c4e13d8753d300a1c59",
    "dropIds": ["invalidDropId"],
    "pickupDate": "2024-09-22"
}
```

```json
//Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Invalid dropId"
}
```


# Assign Drop

<mark style="color:blue;">`GET`</mark> `{baseUrl}/drops/assign`

Assigns drop to partner

#### Headers

| Name                                          | Type   | Description |
| --------------------------------------------- | ------ | ----------- |
| app\_id<mark style="color:red;">\*</mark>     | String |             |
| app\_secret<mark style="color:red;">\*</mark> | String |             |

#### Request Body

| Name                                        | Type           | Description       |
| ------------------------------------------- | -------------- | ----------------- |
| dropIds<mark style="color:red;">\*</mark>   | Array\<String> | Array of Drop Ids |
| partnerId<mark style="color:red;">\*</mark> | String         | PartnerId         |

{% tabs %}
{% tab title="200: OK Drop assigned successfully" %}

{% endtab %}

{% tab title="400: Bad Request Invalid partnerId" %}

{% endtab %}
{% endtabs %}

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/drops/assign
Method: POST
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

### Sample Success Data

<pre class="language-json"><code class="lang-json"><strong>//Sample Request Body
</strong>{
    "partnerId": "{{partnerId}}",
    "dropIds": ["65b8c2c734d8cd1be099fd45"]
}
</code></pre>

```json
//Sample Response Body - 200 OK
{
    "status": true,
    "message": "Drop assigned successfully",
    "data": [
        {}
    ]
}
```

### Sample Error Data

```json
//Sample Request Body
{
    "partnerId": "invalid id",
    "dropIds": ["65b8c2c734d8cd1be099fd45"]
}
```

```json
//Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Invalid partnerId"
}
```


# Status Tracking


# Track Delivery Status

## Track Delivery Status

<mark style="color:blue;">`GET`</mark> `{baseUrl}/deliveries/track`

Track the status and get details of a delivery using the batch ID or tracking ID

#### Query Parameters

| Name       | Type   | Description                            |
| ---------- | ------ | -------------------------------------- |
| batchId    | string | The batch ID of the delivery request   |
| trackingId | string | The tracking ID of a specific delivery |

#### Headers

| Name                                          | Type   | Description |
| --------------------------------------------- | ------ | ----------- |
| app\_id<mark style="color:red;">\*</mark>     | String |             |
| app\_secret<mark style="color:red;">\*</mark> | String |             |

{% tabs %}
{% tab title="200 Delivery details retrieved." %}

```json
{
  "status": true,
  "message": "Deliveries successfully retrieved",
  "data": {
    "id": "65fc04820686cd2854e20bf0",
    "trackingUrl": "https://app.sendstack.africa/tracking?batchId=OLN9HI",
    "paymentStatus": "PAID",
    "paymentSource": "self",
    "messageDropAfterPickup": false,
    "batchId": "OLN9HI",
    "initiator": "65fc04750686cd2854e20a92",
    "isSameDayDelivery": false,
    "hasLockerBooking": false,
    "totalAmount": 1500,
    "manager": {
      "id": "65fc04750686cd2854e20a92",
      "name": "Ginger Dibbert",
      "email": "makayla_corwin15@gmail.com"
    },
    "pickup": {
      "address": "8504 Collier Plaza",
      "locationCode": "01",
      "pickupName": "Karen Casper",
      "pickupNumber": "+2348010988866",
      "pickupDate": "2024-03-22T00:00:00.000Z",
      "state": "Lagos",
      "zone": "zone_1",
      "lga": "Location 1",
      "long": 3.345006,
      "lat": 6.602928
    },
    "drops": [
      {
        "id": "65fc07e59d2de436cc08b56a",
        "trips": [],
        "processed": true,
        "customFields": { 
            "pickup": {
                "pickup_house_number": 1
            },
            "drop": {
                "dropoff_house_number": 2
            }
         }
        //...truncated for brevity
      }
    ],
    "createdAt": "2024-03-21T09:57:22.070Z"
  }
}

```

{% endtab %}

{% tab title="400 Invalid batchId or trackingId" %}

```
{
    "status": false,
    "message": "Delivery details not found",
    "data": {}
}
```

{% endtab %}
{% endtabs %}

### Sample Success Data

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/deliveries/track?batchId={{batchId}}&trackingId={{trackingId}}
NB: Replace {{batchId}} and {{trackingId}} with their respective string values
Method: GET
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

```json
//Sample Response Body - 200 OK
{
  "status": true,
  "message": "Deliveries successfully retrieved",
  "data": {
    "id": "65fc04820686cd2854e20bf0",
    "trackingUrl": "https://app.sendstack.africa/tracking?batchId=OLN9HI",
    "paymentStatus": "PAID",
    "paymentSource": "self",
    "messageDropAfterPickup": false,
    "batchId": "OLN9HI",
    "initiator": "65fc04750686cd2854e20a92",
    "isSameDayDelivery": false,
    "hasLockerBooking": false,
    "totalAmount": 1500,
    "manager": {
      "id": "65fc04750686cd2854e20a92",
      "name": "Ginger Dibbert",
      "email": "makayla_corwin15@gmail.com"
    },
    "pickup": {
      "address": "8504 Collier Plaza",
      "locationCode": "01",
      "pickupName": "Karen Casper",
      "pickupNumber": "+2348010988866",
      "pickupDate": "2024-03-22T00:00:00.000Z",
      "state": "Lagos",
      "zone": "zone_1",
      "lga": "Location 1",
      "long": 3.345006,
      "lat": 6.602928
    },
    "drops": [
      {
        "id": "65fc07e59d2de436cc08b56a",
        "trips": [],
        "processed": true,
        //...truncated for brevity
      }
    ],
    "createdAt": "2024-03-21T09:57:22.070Z"
  }
}

```

### Sample Error Data

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/deliveries/track?batchId=invalidBatchId&trackingId=invalidTrackingId
Method: GET
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

```json
//Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Invalid batchId or trackingId"
}
```


# Messaging


# Send Customer Message

## Add internal notes

<mark style="color:green;">`POST`</mark> `{baseUrl}/drops/publicNotes`

Add a public note on deliveries by providing an array of dropIds.

#### Headers

| Name                                          | Type   | Description |
| --------------------------------------------- | ------ | ----------- |
| app\_id<mark style="color:red;">\*</mark>     | String |             |
| app\_secret<mark style="color:red;">\*</mark> | String |             |

#### Request Body

| Name                                      | Type           | Description |
| ----------------------------------------- | -------------- | ----------- |
| dropIds<mark style="color:red;">\*</mark> | Array\<String> |             |
| note<mark style="color:red;">\*</mark>    | String         |             |

{% tabs %}
{% tab title="200: OK Note added successfully" %}

{% endtab %}

{% tab title="400: Bad Request Invalid drop Id" %}

{% endtab %}
{% endtabs %}

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/drops/publicNotes
Method: POST
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

### Sample Success Data

```json
// Sample Request Body

{ 
    "dropIds": ["65b8c2c734d8cd1be099fd45"], 
    "note": "This is a public info"
}

```

```json
//Sample Response Body - 200 OK
{ 
    "status": true, 
    "message": "Note added successfully", 
    "data": {} 
}
```

### Sample Error Data

```json
// Sample Request Body

{ 
    "dropIds": ["invalidDropId"], 
    "note": "This is a public info"
}

```

```json
//Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Invalid drop Id"
}
```


# Add Private Note

## Add internal notes

<mark style="color:green;">`POST`</mark> `{baseUrl}/drops/internalNotes`

Add private notes on deliveries by providing an array of dropIds.

#### Headers

| Name                                          | Type   | Description |
| --------------------------------------------- | ------ | ----------- |
| app\_id<mark style="color:red;">\*</mark>     | String |             |
| app\_secret<mark style="color:red;">\*</mark> | String |             |

#### Request Body

| Name                                      | Type           | Description |
| ----------------------------------------- | -------------- | ----------- |
| dropIds<mark style="color:red;">\*</mark> | Array\<String> |             |
| note<mark style="color:red;">\*</mark>    | String         |             |

{% tabs %}
{% tab title="200: OK Note added successfully" %}

{% endtab %}

{% tab title="400: Bad Request Invalid dropId" %}

{% endtab %}
{% endtabs %}

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/drops/internalNotes
Method: POST
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

### Sample Success Data

```json
// Sample Request Body
{ 
    "dropIds": ["65b8c2c734d8cd1be099fd45"], 
    "note": "This is a sample note"
}
```

```json
//Sample Response Body - 200 OK
{ 
    "status": true, 
    "message": "Note added successfully", 
    "data": {} 
}
```

### Sample Error Data

```json
//Sample Request Body
{ 
    "dropIds": ["invalidDropId"], 
    "note": "This is a sample note"
}
```

```json
//Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Invalid drop Id"
}
```


# Update note status

Update note by providing the noteId

## Update note

<mark style="color:green;">`POST`</mark> `{baseUrl}/drops/notes/:id`

Update note status by providing the noteId

#### Path Parameters

| Name                                 | Type   | Description |
| ------------------------------------ | ------ | ----------- |
| id<mark style="color:red;">\*</mark> | String |             |

{% tabs %}
{% tab title="200: OK Note added successfully" %}

{% endtab %}

{% tab title="400: Bad Request Invalid dropId" %}

{% endtab %}
{% endtabs %}

### Sample Success Data

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/drops/notes/noteId
NB: Replace noteId with actual noteId
Method: PUT
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

```json
{
    "status": true,
    "message": "Note updated successfuly",
    "data": null
}
```

### Sample Error Data

```
// Sample Request Information
Url: https://sandbox.sendstack.africa/api/v1/drops/notes/invalidNoteId
Method: PUT
Headers: 
 - app_id: "your app_id"
 - app_secret: "your app_secret"
```

```json
//Sample Response Body - 400 Bad Request
{
    "status": false,
    "message": "Invalid note Id"
}
```


# Sample Webhook Payloads


# New delivery

```
{
  "eventType": "order.new", 
  "id": "64e4eb8f450a1b56879f8fa9",
  "trackingUrl": "https://app.sendstack.africa/tracking?batchId=SH1P9A",
  "paymentStatus": "PAID",
  "paymentSource": "self",
  "batchId": "SH1P9A",
  "customerId": "admin",
  "totalAmount": 200,
  "pickup": {
    "address": "24, Ayomide street, Yaba",
    "locationCode": "6BX7",
    "pickupName": "Mex",
    "pickupNumber": "09081111111",
    "pickupDate": "2023-08-22T00:00:00.000Z",
    "lga": "Costain",
    "state": "Lagos"
  },
  "drops": [
    {
      "id": "64e4eb8e450a1b56879f8fa7",
      "status": "PENDING",
      "trackingId": "R7VAS7",
      "externalOrderId": "X1688y",
      "trackingUrl": "https://app.sendstack.africa/tracking?trackingId=R7VAS7",
      "batchId": "SH1P9A",
      "locationCode": "MTW2",
      "address": "35, Queen street, Ikoyi",
      "recipientName": "Tolu",
      "recipientNumber": "09081234353",
      "estimatedPickupWindow": {
        "date": "2023-08-22T00:00:00.000Z",
        "start": "2023-08-22T07:00:00.000Z",
        "end": "2023-08-22T13:00:00.000Z"
      },
      "estimatedDropoffWindow": {
        "date": "2023-08-22T00:00:00.000Z",
        "start": "2023-08-22T13:00:00.000Z",
        "end": "2023-08-22T18:00:00.000Z"
      },
      "pickupOtp": "6440",
      "dropoffOtp": "4205",
      "state": "Lagos",
      "lga": "IGANDO",
      "amount": 200,
      "statusTimestamps": []
    }
  ],
  "createdAt": "2023-08-22T17:08:31.117Z",
  "webhookId": "66faa977606e413be093c02f"
}
```


# Updated delivery status

```
//Assigned
{
  "dropId": "675af64b20b68e11824936d2",
  "batchId": "O83DY9",
  "trackingId": "SOKOAG",
  "externalOrderId": "X1688y",
  "newStatus": "ASSIGNED",
  "partner": {
    "name": "test",
    "phone": "9050707070"
  },
  "eventType": "order.status",
  "webhookId": "675af64d20b68e118249371b"
}
```

```
//ACCEPTED
{
  "dropId": "675af64b20b68e11824936d2",
  "batchId": "O83DY9",
  "trackingId": "SOKOAG",
  "externalOrderId": "X1688y",
  "newStatus": "ACCEPTED",
  "partner": {
    "name": "test",
    "phone": "9050707070"
  },
  "eventType": "order.status",
  "webhookId": "675bee006294f201dee53a52"
}
```

```
//ARRIVED_FOR_PICKUP
{
  "dropId": "675af64b20b68e11824936d2",
  "batchId": "O83DY9",
  "trackingId": "SOKOAG",
  "externalOrderId": "X1688y",
  "newStatus": "ARRIVED_FOR_PICKUP",
  "partner": {
    "name": "test",
    "phone": "9050707070"
  },
  "eventType": "order.status",
  "webhookId": "675bee616294f201dee53bc0"
}
```

```
//PICKED_UP
{
  "dropId": "675af64b20b68e11824936d2",
  "batchId": "O83DY9",
  "trackingId": "SOKOAG",
  "externalOrderId": "X1688y",
  "newStatus": "PICKED_UP",
  "partner": {
    "name": "test",
    "phone": "9050707070"
  },
  "eventType": "order.status",
  "webhookId": "675bef636294f201dee53dc6"
}
```

```
//IN_TRANSIT
{
  "dropId": "675af64b20b68e11824936d2",
  "batchId": "O83DY9",
  "trackingId": "SOKOAG",
  "externalOrderId": "X1688y",
  "newStatus": "IN_TRANSIT",
  "partner": {
    "name": "test",
    "phone": "9050707070"
  },
  "eventType": "order.status",
  "webhookId": "675befde6294f201dee53ee9"
}
```

```
//ARRIVED_FOR_DELIVERY
{
  "dropId": "675af64b20b68e11824936d2",
  "batchId": "O83DY9",
  "trackingId": "SOKOAG",
  "externalOrderId": "X1688y",
  "newStatus": "ARRIVED_FOR_DELIVERY",
  "partner": {
    "name": "test",
    "phone": "9050707070"
  },
  "eventType": "order.status",
  "webhookId": "675bf0316294f201dee53fe3"
}
```

```
//DELIVERED
{
  "dropId": "675af64b20b68e11824936d2",
  "batchId": "O83DY9",
  "trackingId": "SOKOAG",
  "externalOrderId": "X1688y",
  "newStatus": "DELIVERED",
  "partner": {
    "name": "test",
    "phone": "9050707070"
  },
  "eventType": "order.status",
  "webhookId": "675bf0596294f201dee54081"
}
```

```
//REJECTED
{
  "dropId": "675bf2455fab9301e90798c0",
  "batchId": "K9INSO",
  "trackingId": "7CID5T",
  "externalOrderId": "X1688y",
  "newStatus": "REJECTED",
  "partner": {
    "name": "test",
    "phone": "9050707070"
  },
  "eventType": "order.status",
  "webhookId": "675bf2875fab9301e9079a3b"
}
```


# New internal note

```
{
  "eventType": "order.message",
  "dropId": "64f59c54de1a799de32378b2",
  "batchId": "FMFD7K",
  "trackingId": "9SXHHH",
  "externalOrderId": "X1688y",
  "message": "noted g",
  "messageType": "internalNote",
  "webhookId": "66faa977606e413be093c02f"
}
```


# New customer message

```
{
  "eventType": "order.message",
  "dropId": "64f59c54de1a799de32378b2",
  "batchId": "FMFD7K",
  "trackingId": "9SXHHH",
  "externalOrderId": "X1688y",
  "message": "public note ",
  "messageType": "publicNote",
  "webhookId": "66faa977606e413be093c02f"
}
```


# Pickup attempt

```
{
  "eventType": "order.message",
  "dropId": "64f59bdf23d7089ca657b91e",
  "batchId": "U4C7VO",
  "trackingId": "F8553J",
  "externalOrderId": "X1688y",
  "message": "Pickup attempted",
  "messageType": "pickupAttempt",
  "callbackNumber": "08033000221",
  "webhookId": "66faa977606e413be093c02f"
}
```


# Dropoff attempt

```
{
  "eventType": "order.message",
  "dropId": "64f59bdf23d7089ca657b91e",
  "batchId": "U4C7VO",
  "trackingId": "F8553J",
  "externalOrderId": "X1688y",
  "message": "Delivery attempted",
  "messageType": "deliveryAttempt",
  "callbackNumber": "08102345353",
  "webhookId": "66faa977606e413be093c02f"
}
```


# Pickup window updated

```
{
  "eventType": "order.updateWindow",
  "dropId": "64f59bdf23d7089ca657b91e",
  "batchId": "U4C7VO",
  "trackingId": "F8553J",
  "externalOrderId": "X1688y",
  "newWindow": {
    "date": "2023-08-23",
    "start": "2023-08-23T15:00:00.493Z",
    "end": "2023-08-23T17:00:00.493Z"
  },
  "windowType": "pickupWindow",
  "webhookId": "66faa977606e413be093c02f"
}
```


# Delivery window updated

```
{
  "eventType": "order.updateWindow",
  "dropId": "64f59bdf23d7089ca657b91e",
  "batchId": "U4C7VO",
  "trackingId": "F8553J",
  "externalOrderId": "X1688y",
  "newWindow": {
    "date": "2023-08-23",
    "start": "2023-08-23T15:00:00.493Z",
    "end": "2023-08-23T17:00:00.493Z"
  },
  "windowType": "dropoffWindow",
  "webhookId": "66faa977606e413be093c02f"
}
```


# Devices

Endpoints to manage your tracking devices


# Get Devices

Get devices associated with your account

## Get Tracking Devices

<mark style="color:blue;">`GET`</mark> `{{baseUrl}}/devices`

{% tabs %}
{% tab title="200: OK Retrieved devices" %}

```javascript
{
    "status": true,
    "message": "Devices successfully retrieved",
    "data": [
        {
            "id": "6166b0be3dd87d46743a7ca2",
            "name": "Tracker 1", 
            "createdAt": "2025-01-13T11:31:53.401+00:00", 
            "isLost": false, 
            "imei": "864943044600000",
            "batteryLife": "65",
            "totalDistance": 28576459.00235253,
            "online": false,
            "deviceType": "tracker"
        },
        {
            "id": "61a72922fd4492a136466941",
            "name": "Musa phone", 
            "deviceType": "smartphone", 
            "createdAt": "2021-12-01T07:49:54.946Z"
        }
    ]
}
```

{% endtab %}
{% endtabs %}


# Positions


# Get All Positions

Get latest positions for all devices

## Get Device Positions

<mark style="color:blue;">`GET`</mark> `{{baseUrl}}/positions`

{% tabs %}
{% tab title="200: OK Retrieved positions" %}

```javascript
{
    "status": true,
    "message": "Device positions retrieved successfully",
    "data": [
        {
            "deviceId": "67287d2f8faa08541904202b",
            "name": "sample Test",
            "latitude": 6.5010200000000005,
            "longitude": 3.311388333333333,
            "course": 54,
            "altitude": 0,
            "speed": 0,
            "inMotion": false,
            "lastUpdatedTime": "2025-03-12T16:51:58.000+00:00",
            "lastRecordedTime": "2025-03-12T16:51:58.000+00:00",
            "totalDistance": 28576459.00235253,
            "online": false,
            "deviceType": "tracker"
        },
        {
            "deviceId": "61a72922fd4492a136466941",            
            "name": "Musa phone", 
            "latitude": 6.437608333, 
            "longitude": 3.49107, 
            "altitude": 0,
            "speed": 0,
            "lastUpdatedTime": "2025-01-13T11:31:53.401+00:00",
            "deviceType": "smartphone"
        }
    ]
}
```

{% endtab %}
{% endtabs %}


# Get Device Position History

Get position history for a specific devices

## Get Device Position History

<mark style="color:blue;">`GET`</mark> `{{baseUrl}}/positions/:deviceId`

**Path Parameters**

| Name     | Required | Description |
| -------- | -------- | ----------- |
| deviceId | true     | Device id   |

**Query Parameters**

| Name      | Required | Description         | Sample value |
| --------- | -------- | ------------------- | ------------ |
| startTime | true     | The start timestamp | 2025-03-01   |
| endTime   | true     | The end timestamp   | 2025-03-05   |

**Response**

{% tabs %}
{% tab title="200: OK Retrieved positions" %}

```javascript
{
    "status": true,
    "message": "Device positions retrieved successfully",
    "data": {
        "deviceId": "67287d2f8faa08541904202b",
        "name": "sample Test",
        "positionsHistory": [
            {
                "latitude": 6.525101289494111,
                "longitude": 3.3821942643294824,
                "course": 61,
                "altitude": 11.2,
                "speed": 51.32,
                "inMotion": false,
                "distance": 20.957101542293742, //in meters
                "transmissionTime": "2025-03-02T18:58:49.992Z",
                "lastRecordedTime": "2025-03-01T21:21:04.919Z"
            },
            {
                "latitude": 6.531294579490574,
                "longitude": 3.3885202424260226,
                "course": 6,
                "altitude": 2,
                "speed": 42.78,
                "inMotion": false,
                "distance": 1.7472167798488925,
                "transmissionTime": "2025-03-03T04:34:35.150Z",
                "lastRecordedTime": "2025-03-03T21:41:26.590Z"
            },
            {
                "latitude": 6.517738332115701,
                "longitude": 3.3853589853420636,
                "course": 288,
                "altitude": 41.6,
                "speed": 42.25,
                "inMotion": false,
                "distance": 8.93223641924388,
                "transmissionTime": "2025-03-03T10:09:17.217Z",
                "lastRecordedTime": "2025-03-01T12:44:36.246Z"
            },
            {
                "latitude": 6.518986308627487,
                "longitude": 3.3848135101302104,
                "course": 354,
                "altitude": 45.7,
                "speed": 66.29,
                "inMotion": true,
                "distance": 11.262399120416255,
                "transmissionTime": "2025-03-03T16:52:52.675Z",
                "lastRecordedTime": "2025-03-02T19:30:42.831Z"
            },
            {
                "latitude": 6.527298254032401,
                "longitude": 3.37184325408705,
                "course": 40,
                "altitude": 15,
                "speed": 33.82,
                "inMotion": true,
                "distance": 7.329908491155209,
                "transmissionTime": "2025-03-03T21:46:40.485Z",
                "lastRecordedTime": "2025-03-01T23:19:28.507Z"
            }
        ]
    }
}
```

{% endtab %}
{% endtabs %}


# Stream Positions

To stream real-time positions of your tracking devices, establish a websocket connection as described below.

**Request**

* **Endpoint**: `WEBSOCKET {baseUrl}/socket?session=<sessionID>`

**Response**

Once connected, the websocket will stream real-time updates for your tracking devices. The response includes an array of device positions with detailed information.

```json
{
    "positions": [
        {
            "latitude": 6.5010200000000005,
            "longitude": 3.311388333333333,
            "altitude": 0,
            "speed": 0,
            "inMotion": false,
            "lastTransmittedTime": "2025-03-12T16:51:58.000+00:00",
            "deviceId": "67287d2f8faa08541904202b",
            "totalDistance": 28576459.00235253,
            "online": false,
            "deviceType": "tracker",
            "batteryLife": 100,
            "course": 54,
            "lastRecordedTime": "2025-03-12T16:51:58.000+00:00",
            "name": "sample Test"
        },
        {
            "latitude": 22.64522666666667,
            "longitude": 114.01978666666668,
            "altitude": 0,
            "speed": 0,
            "inMotion": false,
            "lastTransmittedTime": "2024-11-28T08:45:08.000+00:00",
            "deviceId": "67483ddf2a95ecc18167ff91",
            "totalDistance": 28576459.00235253,
            "online": false,
            "deviceType": "tracker",
            "course": 222,
            "lastRecordedTime": "2024-11-28T08:45:08.000+00:00",
            "name": "renamed tracker 3"
        },
        {
            "deviceId": "6780092e89b7bd4c6b0a82be",
            "latitude": 6.437638333333333,
            "longitude": 3.491035,
            "altitude": 0,
            "speed": 0,
            "inMotion": false,
            "lastTransmittedTime": "2025-03-12T19:58:56.000+00:00",
            "batteryLife": 50,
            "totalDistance": 28576459.00235253,
            "online": false,
            "deviceType": "tracker",
            "course": 0,
            "lastRecordedTime": "2025-03-12T19:58:56.000+00:00",
            "name": "Test tracker"
        }
    ]
}
```


# Get Total Distance

Get total distance travelled

## Get Total Distance

<mark style="color:blue;">`GET`</mark> `{{baseUrl}}/tracker/:deviceId/totalDistance`

**Path Parameters**

| Name     | Required | Description |
| -------- | -------- | ----------- |
| deviceId | true     | Device id   |

**Query Parameters**

| Name      | Required | Description         | Sample value |
| --------- | -------- | ------------------- | ------------ |
| startTime | true     | The start timestamp | 2025-03-01   |
| endTime   | true     | The end timestamp   | 2025-03-05   |

**Response**

{% tabs %}
{% tab title="200: OK Retrieved positions" %}

```javascript
{
    "status": true,
    "message": "Device total distance successfully retrieved",
    "data": {
        "deviceId": "67287d2f8faa08541904202b",
        "name": "sample Test",
        "totalDistance": 517.957101542293742 //in meters
    }
}
```

{% endtab %}
{% endtabs %}


# Geofences

**Geofences** are designated geographic areas that you can define. Once created, these areas can trigger **geofence entry ("geofence in")** and **geofence exit ("geofence out")** events. When a tracker enters or exits the defined area, the corresponding event is emitted.

Our API uses the **POLYGON** method to define geofences. You must ensure that the polygon coordinates accurately map to the intended area during creation.

**Example of a polygon geofence:**

```
POLYGON ((6.57955384860341 3.428432957806109, 6.4082822454249 3.44384423420928, 6.421780818038414 3.5975749615640495, 6.574921615748536 3.6404239396161984, 6.57955384860341 3.428432957806109))
```

Geofences can also be updated after creation.


# Create Geofence

### Create Geofence

This endpoint creates a geofence

<mark style="color:green;">`POST`</mark> `{baseUrl}/geofence`

#### Request Body

| Field         | Type     | Required | Description          |
| ------------- | -------- | -------- | -------------------- |
| `name`        | `string` | ✅        | Name of geofence.    |
| `description` | `string` | ❌        | Geofence description |
| `area`        | `string` | ✅        | Geofence area        |

#### Sample Success Data

```
//Sample Request Body
{
    "name": "Test_Island_Geofence",
    "description": "Island coverage locations",
    "area": "POLYGON ((6.57955384860341 3.428432957806109, 6.4082822454249 3.44384423420928, 6.421780818038414 3.5975749615640495, 6.574921615748536 3.6404239396161984, 6.57955384860341 3.428432957806109))"
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Geofence successfully created",
    "data": {}
}
```


# Update Geofence

### Update Geofence

This endpoint updates a geofence

<mark style="color:green;">`PUT`</mark> `{baseUrl}/geofence/:geofenceId`

#### Request Body

| Field         | Type     | Required | Description          |
| ------------- | -------- | -------- | -------------------- |
| `name`        | `string` | ✅        | Name of geofence.    |
| `description` | `string` | ❌        | Geofence description |
| `area`        | `string` | ✅        | Geofence area        |

#### Sample Success Data

```
//Sample Request Body
{
    "name": "Test_Island_Geofence",
    "description": "Island coverage locations",
    "area": "POLYGON ((6.57955384860341 3.428432957806109, 6.4082822454249 3.44384423420928, 6.421780818038414 3.5975749615640495, 6.574921615748536 3.6404239396161984, 6.57955384860341 3.428432957806109))"
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Geofence successfully updated",
    "data": {}
}
```


# Fetch Geofences

### Fetch geofences

Fetches all geofences

<mark style="color:green;">`GET`</mark> `{baseUrl}/geofence`

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Geofences successfully retrieved",
    "data": [
        {
            "id": "67ceebdba95c9d18a4c39333",
            "name": "Testt_Island_Geofence",
            "description": "Island coverage locations",
            "area": "POLYGON ((6.57955384860341 3.428432957806109, 6.4082822454249 3.44384423420928, 6.421780818038414 3.5975749615640495, 6.574921615748536 3.6404239396161984, 6.57955384860341 3.428432957806109))"
        }
    ]
}
```


# Fetch Single Geofence

### Fetch single geofence

Fetches a single geofence

<mark style="color:green;">`GET`</mark> `{baseUrl}/geofence/:geofenceId`

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Geofence successfully retrieved",
    "data": {
        "id": "67ceebdba95c9d18a4c39333",
        "name": "Testt_Island_Geofence",
        "description": "Island coverage locations",
        "area": "POLYGON ((6.57955384860341 3.428432957806109, 6.4082822454249 3.44384423420928, 6.421780818038414 3.5975749615640495, 6.574921615748536 3.6404239396161984, 6.57955384860341 3.428432957806109))"
    }
}
```


# Events

### Overview

You can assign multiple events to your trackers, and when an event is triggered, notifications are sent based on your configuration. By default, webhooks are always triggered, but you can customize additional notification channels (such as email, SMS, or in-app notifications) during event creation.

Events can also be updated after they have been created.

### **Supported Events**

The following tracker events are currently supported:

* [Overspeed event](/live-tracking-apis/events/create-events/overspeed-event)
* [Low battery event](/live-tracking-apis/events/create-events/low-battery-event)
* [No movement event](/live-tracking-apis/events/create-events/no-movement-event)
* [No signal event](/live-tracking-apis/events/create-events/no-signal-event)
* [Geofence in event](/live-tracking-apis/events/create-events/geofence-in-event)
* [Geofence out event](/live-tracking-apis/events/create-events/geofence-out-event)


# Create Events

#### **Create Event**

This section provides detailed instructions on how to create tracker events. Each event type has specific configurations that determine when it is triggered and how notifications are sent. By default, webhooks are enabled for all events, but you can customize additional notification channels during event creation.


# Overspeed Event

### Overview

Overspeed event gets triggered when the tracker exceeds a specified speed limit.

### Create overspeed event

This endpoint allows you to add an overspeed event to a tracker. When the event is triggered, notifications are sent to recipients based on the event notification configuration.

<mark style="color:green;">`POST`</mark> `{baseUrl}/tracker/event`

#### Request Body

| Field                               | Type     | Required | Description                                                                         |
| ----------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------- |
| `trackerId`                         | `string` | ✅        | The unique ID of the tracker.                                                       |
| `eventType`                         | `string` | ✅        | The type of event to be created. (For overspeed, use `"OVER_SPEEDING_WARNING"`)     |
| `eventConfig`                       | `object` | ✅        | Configuration settings for the event.                                               |
| `eventConfig.speedLimit`            | `number` | ✅        | The speed limit in km/hr) that triggers the event.                                  |
| `eventNotification`                 | `object` | ❌        | Optional notification settings for the event.                                       |
| `eventNotification.acceptedMediums` | `array`  | ❌        | A list of notification channels and their settings.                                 |
| `eventNotification.status`          | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"` |

#### Sample Success Data

```
//Sample Request Body
{
    "trackerId": "6780092e89b7bd4c6b0a82be",
    "eventType": "OVER_SPEEDING_WARNING",
    "eventConfig": {
        "speedLimit": 15
    },
    "eventNotification": { //optional
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully created",
    "data": {}
}
```


# Low Battery Event

The **Low Battery Event** is triggered when the tracker's battery level is low.

* This event is automatically added when a tracker is added.
* By default, a **webhook** and an **email notification** (sent to the control business email) are enabled.
* You can customize the event notifications, such as modifying notification channels or recipients, using the **Update Event** endpoint.

For more details on updating events, refer to the Update Event API Documentation


# No Movement Event

### Overview

No movement event gets triggered when no movement is detected for a specified period.

### Create no movement event

This endpoint allows you to add a no movement event to a tracker. When the event is triggered, notifications are sent to recipients based on the event notification configuration.

<mark style="color:green;">`POST`</mark> `{baseUrl}/tracker/event`

#### Request Body

| Field                                | Type     | Required | Description                                                                         |
| ------------------------------------ | -------- | -------- | ----------------------------------------------------------------------------------- |
| `trackerId`                          | `string` | ✅        | The unique ID of the tracker.                                                       |
| `eventType`                          | `string` | ✅        | The type of event to be created. (For no movement event, use `"NO_MOVEMENT_ALERT"`) |
| `eventConfig`                        | `object` | ✅        | Configuration settings for the event.                                               |
| `eventConfig.minimumParkingDuration` | `number` | ✅        | The delay (in minutes) before the event is triggered after it stops.                |
| `eventNotification`                  | `object` | ❌        | Optional notification settings for the event.                                       |
| `eventNotification.acceptedMediums`  | `array`  | ❌        | A list of notification channels and their settings.                                 |
| `eventNotification.status`           | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"` |

#### Sample Success Data

```
//Sample Request Body
{
    "trackerId": "6780092e89b7bd4c6b0a82be",
    "eventType": "NO_MOVEMENT_ALERT",
    "eventConfig": {
        "minimumParkingDuration": 15
    },
    "eventNotification": { //optional
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully created",
    "data": {}
}
```


# No Signal Event

### Overview

No signal event gets triggered when the tracker loses connection or signal.

### Create no signal event

This endpoint allows you to add a no signal event to a tracker. When the event is triggered, notifications are sent to recipients based on the event notification configuration.

<mark style="color:green;">`POST`</mark> `{baseUrl}/tracker/event`

#### Request Body

| Field                               | Type     | Required | Description                                                                         |
| ----------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------- |
| `trackerId`                         | `string` | ✅        | The unique ID of the tracker.                                                       |
| `eventType`                         | `string` | ✅        | The type of event to be created. (For no signal alert, use `"NO_SIGNAL_ALERT"`)     |
| `eventNotification`                 | `object` | ❌        | Optional notification settings for the event.                                       |
| `eventNotification.acceptedMediums` | `array`  | ❌        | A list of notification channels and their settings.                                 |
| `eventNotification.status`          | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"` |

#### Sample Success Data

```
//Sample Request Body
{
    "trackerId": "6780092e89b7bd4c6b0a82be",
    "eventType": "NO_SIGNAL_ALERT",
    "eventNotification": { //optional
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully created",
    "data": {}
}
```


# Geofence In Event

### Overview

Geofence in event gets triggered when the tracker enters a designated geofenced area.

### Create geofence in event

This endpoint allows you to add a geofence in event to a tracker. When the event is triggered, notifications are sent to recipients based on the event notification configuration.

<mark style="color:green;">`POST`</mark> `{baseUrl}/tracker/event`

#### Request Body

| Field                               | Type     | Required | Description                                                                                                                        |
| ----------------------------------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `trackerId`                         | `string` | ✅        | The unique ID of the tracker.                                                                                                      |
| `eventType`                         | `string` | ✅        | The type of event to be created. (For geofence in event, use `"GEOFENCE_IN"`)                                                      |
| geofenceIds                         | `array`  | ✅        | Array of geofence ids. Instructions on how to create a geofence can be found [here](/live-tracking-apis/geofences/create-geofence) |
| `eventNotification`                 | `object` | ❌        | Optional notification settings for the event.                                                                                      |
| `eventNotification.acceptedMediums` | `array`  | ❌        | A list of notification channels and their settings.                                                                                |
| `eventNotification.status`          | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"`                                                |

#### Sample Success Data

```
//Sample Request Body
{
    "trackerId": "6780092e89b7bd4c6b0a82be",
    "eventType": "GEOFENCE_IN",
    "geofenceIds": [ "67ceebdba95c9d18a4c39333" ],
    "eventNotification": { //optional
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully created",
    "data": {}
}
```


# Geofence Out Event

### Overview

Geofence out event gets triggered when the tracker exits a designated geofenced area.

### Create geofence in event

This endpoint allows you to add a geofence in event to a tracker. When the event is triggered, notifications are sent to recipients based on the event notification configuration.

<mark style="color:green;">`POST`</mark> `{baseUrl}/tracker/event`

#### Request Body

| Field                               | Type     | Required | Description                                                                                                                        |
| ----------------------------------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `trackerId`                         | `string` | ✅        | The unique ID of the tracker.                                                                                                      |
| `eventType`                         | `string` | ✅        | The type of event to be created. (For geofence out event, use `"GEOFENCE_OUT"`                                                     |
| geofenceIds                         | `array`  | ✅        | Array of geofence ids. Instructions on how to create a geofence can be found [here](/live-tracking-apis/geofences/create-geofence) |
| `eventNotification`                 | `object` | ❌        | Optional notification settings for the event.                                                                                      |
| `eventNotification.acceptedMediums` | `array`  | ❌        | A list of notification channels and their settings.                                                                                |
| `eventNotification.status`          | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"`                                                |

#### Sample Success Data

```
//Sample Request Body
{
    "trackerId": "6780092e89b7bd4c6b0a82be",
    "eventType": "GEOFENCE_OUT",
    "geofenceIds": [ "67ceebdba95c9d18a4c39333" ],
    "eventNotification": { //optional
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully created",
    "data": {}
}
```


# Update Events

This section explains how to modify existing tracker events. After an event is created, you can update its configuration, including notification settings and enabled channels. This allows for greater flexibility in managing event alerts.

Use the update functionality to customize how and where notifications are sent, ensuring the right recipients are informed when an event is triggered.


# Overspeed Event

### Update overspeed event

This endpoint allows you to update an overspeed event associated to a tracker.&#x20;

<mark style="color:green;">`PATCH`</mark> `{baseUrl}/tracker/event/:eventId`

#### Request Body

| Field                               | Type     | Required | Description                                                                         |
| ----------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------- |
| `eventConfig`                       | `object` | ❌        | Configuration settings for the event.                                               |
| `eventConfig.speedLimit`            | `number` | ❌        | The speed limit in km/hr) that triggers the event.                                  |
| `eventNotification`                 | `object` | ❌        | Optional notification settings for the event.                                       |
| `eventNotification.acceptedMediums` | `array`  | ❌        | A list of notification channels and their settings.                                 |
| `eventNotification.status`          | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"` |

#### Sample Success Data

```
//Sample Request Body
{
    "eventConfig": {
        "speedLimit": 25
    },
    "eventNotification": { 
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully updated",
    "data": {}
}
```


# Low Battery Event

### Update Low battery event

This endpoint allows you to update a low battery event associated to a tracker.&#x20;

<mark style="color:green;">`PATCH`</mark> `{baseUrl}/tracker/event/:eventId`

#### Request Body

| Field                               | Type     | Required | Description                                                                         |
| ----------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------- |
| `eventNotification`                 | `object` | ❌        | Optional notification settings for the event.                                       |
| `eventNotification.acceptedMediums` | `array`  | ❌        | A list of notification channels and their settings.                                 |
| `eventNotification.status`          | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"` |

#### Sample Success Data

```
//Sample Request Body
{
    "eventNotification": { 
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully updated",
    "data": {}
}
```


# No Movement Event

### Update No movement event

This endpoint allows you to update a no movement event associated to a tracker.&#x20;

<mark style="color:green;">`PATCH`</mark> `{baseUrl}/tracker/event/:eventId`

#### Request Body

| Field                                | Type     | Required | Description                                                                         |
| ------------------------------------ | -------- | -------- | ----------------------------------------------------------------------------------- |
| `eventConfig`                        | `object` | ❌        | Configuration settings for the event.                                               |
| `eventConfig.minimumParkingDuration` | `number` | ❌        | The delay (in minutes) before the event is triggered after it stops.                |
| `eventNotification`                  | `object` | ❌        | Optional notification settings for the event.                                       |
| `eventNotification.acceptedMediums`  | `array`  | ❌        | A list of notification channels and their settings.                                 |
| `eventNotification.status`           | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"` |

#### Sample Success Data

```
//Sample Request Body
{
    "eventConfig": {
        "minimumParkingDuration": 20
    },
    "eventNotification": { 
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully updated",
    "data": {}
}
```


# No Signal Event

### Update Low battery event

This endpoint allows you to update a no signal event associated to a tracker.&#x20;

<mark style="color:green;">`PATCH`</mark> `{baseUrl}/tracker/event/:eventId`

#### Request Body

| Field                               | Type     | Required | Description                                                                         |
| ----------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------- |
| `eventNotification`                 | `object` | ❌        | Optional notification settings for the event.                                       |
| `eventNotification.acceptedMediums` | `array`  | ❌        | A list of notification channels and their settings.                                 |
| `eventNotification.status`          | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"` |

#### Sample Success Data

```
//Sample Request Body
{
    "eventNotification": { 
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully updated",
    "data": {}
}
```


# Geofence In Event

### Update Geofence in event

This endpoint allows you to update a geofence in event associated to a tracker.&#x20;

<mark style="color:green;">`PATCH`</mark> `{baseUrl}/tracker/event/:eventId`

#### Request Body

| Field                               | Type     | Required | Description                                                                         |
| ----------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------- |
| geofenceIds                         | `array`  | ❌        | Array of geofence ids.                                                              |
| `eventNotification`                 | `object` | ❌        | Optional notification settings for the event.                                       |
| `eventNotification.acceptedMediums` | `array`  | ❌        | A list of notification channels and their settings.                                 |
| `eventNotification.status`          | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"` |

#### Sample Success Data

```
//Sample Request Body
{
    "geofenceIds": [ "67ceebdba95c9d18a4c39333" ],
    "eventNotification": { 
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully updated",
    "data": {}
}
```


# Geofence Out Event

### Update Geofence out event

This endpoint allows you to update a geofence out event associated to a tracker.&#x20;

<mark style="color:green;">`PATCH`</mark> `{baseUrl}/tracker/event/:eventId`

#### Request Body

| Field                               | Type     | Required | Description                                                                         |
| ----------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------- |
| geofenceIds                         | `array`  | ❌        | Array of geofence ids.                                                              |
| `eventNotification`                 | `object` | ❌        | Optional notification settings for the event.                                       |
| `eventNotification.acceptedMediums` | `array`  | ❌        | A list of notification channels and their settings.                                 |
| `eventNotification.status`          | `string` | ❌        | Status of the notification configuration. Accepts either `"inactive"` or `"active"` |

#### Sample Success Data

```
//Sample Request Body
{
    "geofenceIds": [ "67ceebdba95c9d18a4c39333" ],
    "eventNotification": { 
      "acceptedMediums": [
        {
          "channel": "email",
          "isEnabled": true,
          "defaultEmail": [ "test@gmail.com" ]
        },
        {
          "channel": "sms",
          "isEnabled": true,
          "defaultPhone": [ "+2349088888888" ]
        },
        {
          "channel": "inApp",
          "isEnabled": false
        }
      ],
      "status": "active"
    }
}
```

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker event successfully updated",
    "data": {}
}
```


# Fetch Events

### Fetch Events

Fetches all events associated to a tracker

<mark style="color:green;">`GET`</mark> `{baseUrl}/tracker-events?trackerId={trackerId}`

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker events successfully retrieved",
    "data": [
        {
            "id": "67b41824ac676a37981df66b",
            "eventType": "OVER_SPEEDING_WARNING",
            "eventConfig": {
                "speedLimit": 15
            },
            "eventNotification": {
                "acceptedMediums": [
                    {
                        "channel": "email",
                        "isEnabled": true,
                        "defaultEmail": ["test@gmail.com"]
                    },
                    {
                        "channel": "sms",
                        "isEnabled": false,
                        "defaultPhone": [ "+2349088888888" ]
                    },
                    {
                        "channel": "inApp",
                        "isEnabled": true
                    }
                ],
                "status": "active"
            }
        },
    ]
}
```


# Fetch Single Event

### Fetch Single event

Fetches a single event

<mark style="color:green;">`GET`</mark> `{baseUrl}/tracker-events/:eventId`

#### Sample Success Response

```
//Sample success response
{
    "status": true,
    "message": "Tracker events successfully retrieved",
    "data": [
        {
            "id": "67b41824ac676a37981df66b",
            "eventType": "OVER_SPEEDING_WARNING",
            "eventConfig": {
                "speedLimit": 15
            },
            "eventNotification": {
                "acceptedMediums": [
                    {
                        "channel": "email",
                        "isEnabled": true,
                        "defaultEmail": ["test@gmail.com"]
                    },
                    {
                        "channel": "sms",
                        "isEnabled": false,
                        "defaultPhone": [ "+2349088888888" ]
                    },
                    {
                        "channel": "inApp",
                        "isEnabled": true
                    }
                ],
                "status": "active"
            }
        },
    ]
}
```




---

[Next Page](/llms-full.txt/1)

