openapi: 3.0.1
info:
  title: Ninja Van API
  version: 4.1.0
  description: |+

    # Introduction
    Ninja Van is a tech-enabled express delivery company providing hassle-free delivery services for businesses of all sizes across Southeast Asia. 
    This portal provides technical guidance on how to get started with and use Ninja Van APIs for your business scenarios.
    
    The portal provides documentation for the following APIs and services:
    * [Order API](#tag/Order-API). Create delivery orders, generate waybills, and cancel orders.
    * [Tariff API](#tag/Tariff-API). Get a delivery fee estimate for an order.
    * [PUDO API](#tag/PUDO-API). Embed address selection information for Ninja Points into your e-commerce website.
    * [PUDO partners API](#tag/PUDO-partners-API). As a PUDO partner, use this API to trigger parcel collection and drop-off, and receive updates on the success of operational processes at your Ninja Points.
    * [Tracking API](#tag/Tracking-API). Retrieve tracking statuses for a list of parcels or for a single parcel.
    * [Webhooks](#tag/Webhooks). Receive updates on successful changes in the lifespan of an order.

    All Ninja Van APIs are RESTful APIs.

    ## Integration process
    1. You (shipper) inform us (Ninja Van) that you would like to integrate with our APIs.
    2. You and your Account Manager settle the commercial agreements.
    3. Your Account Manager creates your Ninja Dashboard account in the sandbox environment, and sends you an on-boarding email.
    4. To access the production APIs, you need to build an integration on your end, and submit three order creation requests in sandbox for our [developer support team](#section/Introduction/Contact-information) to review.
    5. You generate client credentials—Client ID and Client Key—in your sandbox Ninja Dashboard account.
    6. Using the sandbox client credentials, you generate an access token for the sandbox environment by calling the OAuth API.
    7. You create an order in the sandbox environment to verify that the integration works.
    8. If the order is created successfully, you make three order creation requests in sandbox, and submit the tracking IDs (TIDs) to our developer support team.
    9. Our developer support team reviews the requests. If the payload meets the requirements, then you're granted access to the order creation endpoint in production.
    10. You generate a new Client ID and Client Key in your production Ninja Dashboard account.
    11. You generate an access token for the production environment.
    12. You start using the APIs for delivery by making real-life order creation requests in production.

    For account-related issues, please contact your Account Manager. 
    For technical issues related to API integration, send an email to [developer support](#section/Introduction/Contact-information).

    ### Shippers operating in multiple countries
    For shippers operating in multiple countries, the audit process needs to be initiated by the Account Manager and has to be done separately for each country of operation.

    ### Shippers with multiple accounts for the same shipper
    For shippers who plan to have multiple accounts for the same shipper in the Ninja Van platform, we advise you to enroll as a marketplace shipper. 
    Otherwise, your request to access production APIs with several accounts of the same shipper may be rejected after submission.

    ## Authentication
    Ninja Van APIs use the OAuth 2.0 protocol for authentication and authorization.

    To begin, obtain OAuth 2.0 client credentials from the developer settings in your Ninja Dashboard account. 
    Then use your client app to request an access token, extract the token from the response, and send it to a Ninja Van API that you want to access.
    
    However, Ninja Van APIs in the sandbox environment and production environment require different access tokens. 
    The reason for is that the Ninja Dashboard accounts in the two environments are different, and they use different credentials.

    This means that you need to use the sandbox access token for testing your API requests and submitting sample orders for an integration audit. 
    Once we grant you access to production, you'll need to generate a new access token in your production Dashboard account.
    
    ### Client credentials
    The credentials are available in your Ninja Dashboard account, under **Settings > IT Settings**.
    For more information, see [Get access to sandbox](#section/Get-started/Get-access-to-sandbox) and [Get access to production](#section/Get-started/Get-access-to-production).

    ### Access token lifetime
    Access tokens have limited lifetimes. Five minutes before your access token expires, or if a request to a Ninja Van API returns an HTTP `401` status code, generate a new access token.    

    ## Integration requirements

    ### OAuth API
    [OAuth 2.0 token](#tag/OAuth-API/paths/~1{countryCode}~12.0~1oauth~1access_token/post) endpoint:
    * You MUST cache the access token and its expiration timestamp.
    
    ### Order API
    [Order creation](#tag/Order-API/paths/~1{countryCode}~14.1~1orders/post) and [order cancellation](#tag/Order-API/paths/~1{countryCode}~12.2~1orders~1{trackingNo}/delete) endpoints:
    * If the API returns an HTTP `5xx` status code, you SHOULD retry an order creation request.
    * If the API returns an HTTP `4xx` status code, you SHOULD log the response for debugging.
    * You SHOULD NOT retry the same order creation request without fixing the error specified in the response.
    
    [Waybill generation](#tag/Order-API/paths/~1{countryCode}~12.0~1reports~1waybill/get) endpoint:
    
    You MUST cache the waybill upon generation and avoid excessive calls to this endpoint.

    ### Tracking API
    [Get events for single parcel](#tag/Tracking-API/paths/~1{countryCode}~11.0~1orders~1tracking-events~1{trackingNumber}/get) 
    and 
    [Get events for list of parcels](#tag/Tracking-API/paths/~1{countryCode}~11.0~1orders~1tracking-events/get) endpoints.
    * You SHOULD NOT use these endpoints as a primary mechanism to get parcel status updates.
    * You SHOULD subscribe to our webhook for parcel status updates and only use these endpoints when there is a failure to receive webhooks.
    * In order for us to review your use case, please provide a system flow diagram that shows how the APIs will be integrated with your systems. The trigger points for which the APIs are being called should be clearly shown in the diagram.

    ## Base URL by environment
    ### Production base URL
    * URL: `https://api.ninjavan.co`
    * URL with country code: `https://api.ninjavan.co/{countryCode}`

    ### Sandbox base URL
    * URL: `https://api-sandbox.ninjavan.co`
    * URL with country code: `https://api-sandbox.ninjavan.co/sg`

    **Note:** Always send your sandbox requests to the Singapore sandbox API. However, you can send localized addresses for the countries that you are testing for. In other words, you can craft the API request as you would for the production environment. The only difference is the URL.

    ## Rate limits
    In order to provide a high quality of service for all customers, our API is rate-limited. 
    If you exceed the rate limit, the API responds with an HTTP `429 API rate limit exceeded` status code, and you get temporarily blocked from accessing the API.
    In this case, retry your request after a few hours.

    The following endpoints are rate-limited:

    * OAuth API: [Request access token](#tag/OAuth-API/paths/~1{countryCode}~12.0~1oauth~1access_token/post)
    * Order API: [Generate waybill](#tag/Order-API/paths/~1{countryCode}~12.0~1reports~1waybill/get)
    * Tariff API: [Get price estimate](#tag/Tariff-API/paths/~1{countryCode}~11.0~1public~1price/post). The limit is 20 calls per minute.
    * Tracking API:
      * [Get events for single parcel](#tag/Tracking-API/paths/~1{countryCode}~11.0~1orders~1tracking-events~1{trackingNumber}/get)
      * [Get events for list of parcels](#tag/Tracking-API/paths/~1{countryCode}~11.0~1orders~1tracking-events/get)
    
    ## Contact information
    To contact the developer support team, send an email to the address that corresponds to your country:

    * Indonesia: [id-devsupport@ninjavan.co](mailto:id-devsupport@ninjavan.co)
    * Malaysia: [my-devsupport@ninjavan.co](mailto:my-devsupport@ninjavan.co)
    * Philippines: [ph-devsupport@ninjavan.co](mailto:ph-devsupport@ninjavan.co)
    * Singapore: [sg-devsupport@ninjavan.co](mailto:sg-devsupport@ninjavan.co)
    * Thailand: [th-devsupport@ninjavan.co](mailto:th-devsupport@ninjavan.co)
    * Vietnam: [vn-devsupport@ninjavan.co](mailto:vn-devsupport@ninjavan.co)
    * China office: [cn-devsupport@ninjavan.co](mailto:cn-devsupport@ninjavan.co)

    # Get started
    
    ## Get access to sandbox
    To build your workflow and test our APIs, you need to have access to the sandbox environment.

    **Note:** All sandbox requests must be sent to the Singapore sandbox API: `https://api-sandbox.ninjavan.co/sg`.

    ### Prerequisites
    * Ninja Van Postpaid Pro account.
    * Access to Ninja Dashboard.

    ### Steps
    1. Open [Ninja Dashboard](https://dashboard-sandbox.ninjavan.co/login-v2) in sandbox.
    2. Enter your email and password, and click **Login**.
    3. Go to **Settings > IT Settings**.
    4. Click **REGENERATE CLIENT ID & KEY**. Your credentials appear under **Client ID** and **Client Key**, respectively.
    5. Pass the credentials to the [OAuth API](#tag/OAuth-API/paths/~1{countryCode}~12.0~1oauth~1access_token/post) to get your access token.
    
    Use this token for testing your requests to Ninja Van APIs in the sandbox environment. This guide provides [sample parameters and payloads](#section/Sample-order-creation-requests) for various business scenarios.
    
    ## Review your integration
    Before granting you access to production, Ninja Van needs to do an audit of your integration. Take these steps to get started:
    1. In the sandbox environment, submit three different order creation requests reflective of the type of orders that you'll be sending in production, with accurate address and other information. 
    These requests must be sent via the integration that you built, and not via Postman or any other API testing tool.
    2. Send an email to [developer support](#section/Introduction/Contact-information) with the tracking numbers of the orders, and the business requirements.
    
    The Ninja Van developer support team will review your requests, and if they meet our requirements, then you'll be granted access to the Order API in production. 
    
    **Note:** Successful completion of the integration audit doesn't automatically grant you the permission to use the waybill generation endpoint. To request access to this endpoint, see [How to request access](#tag/Order-API/paths/~1{countryCode}~12.0~1reports~1waybill/get).

    ### Shippers operating in multiple countries
    For shippers operating in multiple countries, the audit process needs to be initiated by the Account Manager and has to be done separately for each country of operation.
    
    ## Get access to production
    The access token that you used before you were granted access to production, will not work. This is why you need to generate a new token to access the production APIs.

    ### Prerequisites
    * Ninja Van Postpaid Pro account.
    * Access to Ninja Dashboard.

    ### Steps
    1. Open [Ninja Dashboard](https://dashboard.ninjavan.co/login-v2) in production.
    2. Enter your email and password, and click **Login**.
    3. Go to **Settings > IT Settings**.
    4. Click **REGENERATE CLIENT ID & KEY**. Your credentials appear under **Client ID** and **Client Key**, respectively.
    5. Pass the credentials to the [OAuth API](#tag/OAuth-API/paths/~1{countryCode}~12.0~1oauth~1access_token/post) to get your access token.

    Use this token to authenticate your requests to our APIs in the production environment.

  x-logo:
    url: 'https://images.ctfassets.net/iu7xhxelvu8m/6LC1QNfA1NyDGfp8sBN0gK/ef728c17096218198e06e1a3d990804b/nv-logo-top-sg.svg'
    backgroundColor: '#ffffff'
    altText: Ninja Van
servers:
  - url: 'https://api-sandbox.ninjavan.co'
    description: Sandbox server
  - url: 'https://api.ninjavan.co'
    description: Production server
tags:
  - name: OAuth API
    description: The purpose of the OAuth API is to generate an access token for the sandbox and production environments.

  - name: Order API
    description: The purpose of the Order API is to create delivery orders, generate waybills, and cancel orders.

  - name: Tariff API
    description: |+ 
      The purpose of the Tariff API is to provide a delivery fee estimate for an order.

      **Note:** This API is only available for Indonesia.

  - name: PUDO API
    description: |+
      The purpose of the PUDO API is to provide address selection information for Ninja Points that you can embed into your e-commerce website. 
      This way, your customers can collect their parcels from our extensive network of Ninja Points—Ninja Van's partner retail shops and partner automated parcel lockers strategically located in populous and urban areas with high footfall.

      In addition to parcel collection, customers can use Ninja Points to drop-off their return parcels.
      
      For more information about Ninja Points, visit the [Ninja Points website](https://www.ninjavan.co/en-sg/ninja-points).
  
      ## How to use the PUDO API
      1. Make sure that you can properly load the list of addresses from https://api.ninjavan.co/sg/2.0/pudos in JSON format. 
      2. At your site's address entry page, provide another component that enables selection of Ninja Point locations.
      3. After the form retrieves the address, input it into the order in a way that is specific to your system.   

  - name: PUDO partners API
    description: |+
      The PUDO partners API is used to trigger operational processes at external Ninja Points managed by Ninja Van's partners.

  - name: Tracking API
    description: |+
      The purpose of the Tracking API is to retrieve tracking statuses for a list of parcels or for a single parcel.

      The Tracking API is limited in terms of the rate that it can be called, and the number of tracking numbers that you can query in a single request. We recommend the use of this pull-based API as a backup mechanism to employ when webhooks fail to sync information from our servers to yours.    

  - name: Webhooks
    description: |+

      A webhook is a tool for retrieving and storing data from certain events. Ninja Van sends webhooks upon successful status changes in the lifespan of an order. 
      This section covers the key information that you need to receive webhooks from the Ninja Van platform. 

      | Allowed HTTP request | Description                   |
      |--------------------- |-------------------------------|
      | POST                 | Creates or updates a resource. |
      
      

      # Webhook events
  
        | Event                                 | Terminal status?    | Webhook is triggered when... | 
        |---------------------------------------|---------------------|------------------------------|
        | Pending Pickup                        |  NO                 | Order creation is fully processed in our system, and the order is waiting to be picked up. |
        | Successful Pickup                     |  NO                 | Driver picks up the order. |
        | En-route to Sorting Hub               |  NO                 | Order is picked up and is on the way to a sorting hub. |
        | Arrived at Sorting Hub                |  NO                 | Order arrives at a sorting hub for further processing. |
        | Transferred to 3PL                    |  NO                 | Order is handed over to a 3PL for delivery. |
        | Arrived at Origin Hub                 |  NO                 | Order arrives at an origin hub for further processing. |
        | On Vehicle for Delivery               |  NO                 | Driver picks up the parcel from a sorting hub for delivery. |
        | Pending Reschedule                    |  NO                 | Driver marks the order as delivery failed, and the order is waiting to be rescheduled. |
        | Pickup Fail                           |  NO                 | Driver isn't able to pick up the parcel. The order's waiting for a pickup to be rescheduled. |
        | First Attempt Delivery Fail           |  NO                 | Driver isn't able to deliver the parcel and marks the order as delivery failed. |
        | On Vehicle for Delivery (RTS)         |  NO                 | Order is in a van and en-route to the sender after a failed delivery. |
        | Returned to Sender                    |  NO                 | After repeated failed deliveries, the order is sent back to the sender. | 
        | Arrived at Distribution Point         |  NO                 | Parcel has been placed at a Ninja Point for customer collection. | 
        | Return to Sender Triggered            |  NO                 | After repeated failed deliveries, Ninja Van initiates the return flow for the order. | 
        | Pending Pickup at Distribution Point  |  NO                 | Order has been received at a Ninja Point and is waiting to be picked up. | 
        | Van En-route to Pickup                |  NO                 | A van has been dispatched to pick up the return order. | 
        | Parcel Size                           |  NO                 | Parcel size of the order has been changed. | 
        | Parcel Weight                         |  NO                 | Parcel weight of the order has been changed. |
        | Parcel Measurements Update            |  NO                 | Parcel size, or parcel weight, or parcel dimensions have been changed. | 
        | Staging                               |  NO                 | Order is created and is at the staging phase. | 
        | Cross Border Transit                  |  NO                 | Order is in the cross-border leg or, if required, is pending tax payment from the consignee. |
        | Customs Cleared                       |  NO                 | Order is ready for pickup from a customs warehouse. | 
        | Customs Held                          |  NO                 | Order is in a customs clearance exception. | 
        | Completed                             |  YES                | Driver marks the parcel as delivered. |
        | Successful Delivery                   |  YES                | Driver marks the parcel as delivered. |
        | Returned to Sender                    |  YES                | After repeated failed deliveries, the order is sent back to the sender. |
        | Cancelled                             |  YES                | Order is cancelled successfully. |

      # Receive a webhook
  
      After you registered a webhook URL with Ninja Van, we will issue an HTTP POST request to the specified URL every time an event occurs.
      The request's POST parameters contain XML/JSON data relevant to the event that triggered the request.

      ## Example: receive a Pickup Fail webhook

      A Pickup Fail webhook is sent after a pickup for a return or C2C order has failed. The
      webhook contains the Pickup Fail reason in the `comments` field. For example:

      ```
      {
        "comments": "No parcels to pick up at all"
      }
      ```

      This table shows possible reasons.

      | Reason     | Description |
      |-------------------------|----------|
      | Nobody at Location      | No one was present at the pickup location. |
      | Inaccurate Address      | Couldn't find the pickup location. |
      | Parcel Not available    | Item wasn't available at the pickup location, typically because the shipper unexpectedly ran out of stock, or packing got delayed. |
      | Parcel Too Bulky        | Parcel was too bulky to be picked up, typically because the size of the parcel was misreported. |
      | Cancellation Requested  | Pickup was cancelled upon request of the shipper. For some reason, the shipper did not want to fulfill the order at all. |

      The reasons may differ based on the country. For a complete list, contact your Account Manager.

      ## Example: receive a Pending Reschedule webhook

      A Pending Reschedule webhook is sent after a delivery for a normal order has failed. The
      webhook contains the Pending Reschedule reason in the `comments` field. For example:

      ```
      {
      "comments": "Nobody at Location"
      }
      ```

      This list shows possible reasons:
      * Address is correct but customer is not available         
      * Customer requested change of delivery date / time
      * Customer requested change of delivery location
      * Address on AWB is correct
      * Address on AWB is incorrect
      * Address was outside of driver coverage area
      * I had insufficient time to complete all my deliveries
      * Vehicle breakdown
      * Delay due to unexpected traffic conditions
      * Delay due to natural disasters or nationwide emergencies
      * Unattempted - Parcel Issues
      * Office address, closed
      * Office address, open but no one to receive
      * Residential address, but no one to receive
      * Package is fine - unable to collect COD
      * Package is fine - Customer wishes to cancel order
      * Package is defective - Damaged
      * Package is defective - Wrong item inside
      * Location is inaccessible (restricted area)
      * Refused entry by security personnel
      * Driver cannot find location (lat-long issues)
      * Incomplete Address provided (no unit number, block number, etc.)
      * Specified Address was incorrect or Recipient has moved

      The reasons may differ based on the country. For a complete list, contact your Account Manager.

      ## Example: receive an RTS webhook

      A Return to Sender (RTS) webhook is sent after an order was delivered back to the shipper.
      The webhook contains the RTS reason in the `comments` field. For example:

      ```
      {
      "comments": "Return to Sender: Customer delayed beyond delivery period"
      }
      ```

      This table shows possible reasons.        

      | Reason                              | Description |
      |------------------------------------------|-------------|
      | Nobody at Location                       | No one was present at the delivery location.                     |
      | Unable to find Address                   | Couldn't find the delivery location. |
      | Item refused at Doorstep                 | Item was refused at the delivery location. |
      | Refused to pay COD                       | Customer requested for delivery reschedule at a date far in the future (for example, 1 month later) or the delivery failed three times. A failure of three times includes two attempts that were rescheduled by the customer. |
      | Customer delayed beyond delivery period  | Order was cancelled by the shipper. | 

      The reasons may differ based on the country. For a complete list, contact your Account Manager.

      ## Sample webhook payloads
  
      This section shows sample JSON payloads for different webhooks.
  
      **Pending Pickup**
  
      ```
      {
        "shipper_id":11111,
        "status": "Pending Pickup",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T07:00:00+0700",
        "id": "d4dcfea1-5286-4921-8fd3-7ded133f8579",
        "tracking_id": "DX149431"
      }
      ```
  
      **Successful Pickup**
  
      ```
      {
        "shipper_id":11111,
        "status": "Successful Pickup",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T07:02:56+0700",
        "id": "f48f68c1-4665-43df-8d1e-2628bbccfde7",
        "previous_status": "Pending Pickup",
        "tracking_id": "DX149431"
      }
      ```
  
      **En-route to Sorting Hub**
  
      ```
      {
        "shipper_id":11111,
        "status": "En-route to Sorting Hub",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T08:00:08+0800",
        "id": "71acd28a-31d2-4035-b903-6a5fb471c8c1",
        "previous_status": "Pending Pickup",
        "tracking_id": "DX149431"
      }
      ```

      **Arrived at Sorting Hub**
  
      ``` 
      {
        "shipper_id":11111,
        "status": "Arrived at Sorting Hub",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T08:00:03+0800",
        "id": "81bc7a66-5d3d-450c-b2f9-c582488a4977",
        "previous_status": "En-route to Sorting Hub",
        "tracking_id": "DX149431",
        "comments": "PH-CABUYAO-WH-CBY2"
      } 
      ```
  
      **On Vehicle for Delivery**
  
      ```
      {
        "shipper_id":11111,
        "status": "On Vehicle for Delivery",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T07:00:01+0700",
        "id": "5a476158-2b2a-46b7-a7b5-ce89537e183c",
        "previous_status": "Arrived at Sorting Hub",
        "tracking_id": "DX149431",
        "comments": "ID-BANDUNG-Gunung Halu"
      }
      ```
  
      **Pending Reschedule**
  
      ```
      {
        "shipper_id":11111,
        "status": "Pending Reschedule",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T07:01:00+0700",
        "id": "03e14080-5fe0-44b3-8762-4714cd5796c7",
        "previous_status": "On Vehicle for Delivery",
        "tracking_id": "DX149431",
        "comments": "Penerima belum dapat membayar COD"
      }
      ```
  
      **Pickup Fail**
  
      ```
      {
        "shipper_id":11111,
        "status": "Pickup fail",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T08:04:18+0800",
        "id": "38ecffa6-4c5c-4111-9eba-827929281191",
        "previous_status": "Pending Pickup",
        "tracking_id": "DX149431",
        "comments": "No parcels to pick up at all"
      }
      ```
  
      **First Attempt Delivery Fail**
  
      ```
      {
        "shipper_id":11111,
        "status": "First Attempt Delivery Fail",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T08:01:42+0800",
        "id": "d8cf8ba5-c081-4d7e-8eaf-0bbad23e1bc5",
        "previous_status": "On Vehicle for Delivery",
        "tracking_id": "DX149431",
        "comments": "Customer requested change of delivery date / time"
      }
      ```
  
      **On Vehicle for Delivery (RTS)**
  
      ```
      {
        "shipper_id":11111,
        "status": "On Vehicle for Delivery (RTS)",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T07:00:01+0700",
        "id": "bc7a1155-a605-47de-b5ee-4ba3b79b2ac6",
        "previous_status": "Arrived at Sorting Hub",
        "tracking_id": "DX149431",
        "comments": "ID-BANDUNG-Gunung Halu"
      }
      ```

      **Returned to Sender**
  
      ``` 
      {
        "shipper_id":11111,
        "status": "Returned to Sender",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T07:00:31+0700",
        "id": "f7dc0151-e2be-45e3-99aa-686adaf21491",
        "previous_status": "On Vehicle for Delivery",
        "tracking_id": "DX149431"
      }
      ```

      **Arrived at Distribution Point**
  
      ```
      {
        "shipper_id":11111,
        "status": "Arrived at Distribution Point",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T09:31:44+0800",
        "id": "449d32e2-e562-481e-8df6-1a881b5c8ee4",
        "previous_status": "On Vehicle for Delivery",
        "tracking_id": "DX149431"
      }
      ```
  
      **Return to Sender Triggered**
  
      ```
      {
        "shipper_id":11111,
        "status": "Return to Sender Triggered",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-02-28T22:45:28+0700",
        "id": "92327a00-81dd-42e2-9a03-f6dc9661d612",
        "previous_status": "Arrived at Sorting Hub",
        "tracking_id": "DX149431",
        "comments": "Return to sender: Item refused at doorstep"
      }
      ```

      **Pending Pickup at Distribution Point**
  
      ```
      {
        "shipper_id":11111,
        "status": "Pending Pickup at Distribution Point",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T08:00:58+0800",
        "id": "e25da9cc-cb72-45ad-bee9-84aa65d76ff8",
        "previous_status": "Pending Pickup",
        "tracking_id": "DX149431"
      }
      ```

      **Van En-route to Pickup**
  
      ```
      {
        "shipper_id":11111,
        "status": "Van en-route to pickup",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T08:00:34+0800",
        "id": "7018b0a8-c859-43c8-8a2d-d6f74c3022d6",
        "previous_status": "Pending Pickup",
        "tracking_id": "DX149431"
      }
      ```

      **Parcel Size**
  
      ```
      {
        "shipper_id":11111,
        "status": "Parcel Size",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T08:05:03+0800",
        "order_id": "046d4137-cf27-4a27-8dbe-17dc1c36b56d",
        "tracking_id": "DX149431",
        "previous_size": "SMALL",
        "new_size": "LARGE"
      }
      ```
      
      **Parcel Weight**
  
      ```
      {
        "shipper_id":11111,
        "status": "Parcel Weight",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T07:00:47+0700",
        "order_id": "bbeab811-05e1-4528-bad7-28f5ec181a9f",
        "tracking_id": "DX149431",
        "previous_weight": "7.951986465396632",
        "new_weight": "7.582490156526532"
      }
      ```
      **Parcel Measurements Update**
  
      ```
      {
        "shipper_id":11111,
        "status": "Parcel Measurements Update",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T08:00:39+0800",
        "order_id": "5c858b67-c233-4897-ae59-5a78b34afbc4",
        "tracking_id": "DX149431",
        "previous_measurements":{
            "width":10.0,
            "height":10.0,
            "length":10.0,
            "size": "M",
            "volumetric_weight":0.29,
          "measured_weight":1.6918909689116224
        },
        "new_measurements":{
            "width":10.0,
            "height":10.0,
            "length":10.0,
            "size": "S",
            "volumetric_weight":0.29,
            "measured_weight":1.4299700569420262
        }
      }
      ```

      **Completed**
  
      ```
      {
        "shipper_id":11111,
        "status": "Completed",
        "shipper_ref_no": "DX149431",
        "tracking_ref_no": "DX149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T07:00:22+0700",
        "id": "6698241b-18ac-4a3b-a258-a506a85989bd",
        "previous_status": "On Vehicle for Delivery",
        "tracking_id": "DX149431",
        "comments": "Receiver: Astty Oktavia Daeni"
      }
      ```
  
      **Successful Delivery**
  
      ```
      {
        "shipper_id":11111,
        "status": "Successful Delivery",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T08:00:04+0800",
        "id": "2185a893-cd92-42ca-b52a-227cff1bd25a",
        "previous_status": "On Vehicle for Delivery",
        "tracking_id": "DX149431",
        "pod":{
            "type": "RECIPIENT",
            "name": "Claire bautista",
            "identity_number": "",
            "contact": "+639233114933",
            "uri": "",
            "left_in_safe_place": false
          }
      }
      ```
      
      **Cancelled**
  
      ```
      {
        "shipper_id":11111,
        "status": "Cancelled",
        "shipper_ref_no": "149431",
        "tracking_ref_no": "149431",
        "shipper_order_ref_no": "8374",
        "timestamp": "2021-03-01T07:00:44+0700",
        "id": "65994763-20c8-4067-aa44-1da75e8229df",
        "previous_status": "Pending Pickup",
        "tracking_id": "DX149431",
        "comments": "Cancellation reason : API CANCELLATION REQUEST"
      }
      ```
      
      # Verify a webhook
      Ninja Van passes along a digital signature with each request in the `X-Ninjavan-Hmac-SHA256` header. The signature is generated with the HMAC algorithm, using your [Client Secret](#section/Introduction/Authentication) as a key and SHA256 digest mode.

      When you receive a request, compute the HMAC digest according to the
      following algorithm and compare it to the value in the `X-Ninjavan-Hmac-SHA256` header. If they
      match, the webhook was sent from Ninja Van and the data has not been compromised.
  
      Following are webhook verification examples in different languages.
  
      **PHP**
  
      ```
        
      <?php
      
      define('CLIENT_SECRET', 'insert-your-client-secret-here');
      $data = file_get_contents('php://input');
  
      function verify_webhook($data, $hmac_header){
      $calculated_hmac = base64_encode(hash_hmac('sha256', $data , CLIENT_SECRET, true));
      echo $calculated_hmac;
      return ($hmac_header == $calculated_hmac);
      }
   
      $hmac_header = 'Ru6nS2bSEPRaRAq+GM0cMMvVxyFz8sVkKjSI6rD1jgY=';
   
      $verified = verify_webhook($data, $hmac_header);
   
      ?>
       
      ```
  
  
      **Ruby**
  
      ```
        
      require 'openssl'
      require 'base64'
      //>
      hash = OpenSSL::HMAC.digest('sha256', 'INSERT-YOUR-CLIENT-SECRET-HERE', 'INSERT-YOUR-JSON-BODY-HERE')
  
      puts hash
      hashedValue = Base64.encode64(hash)
      puts hashedValue
       
      ```
  
      **Go**
  
      ```
        
      package main
   
      import (
        "crypto/hmac"
        "crypto/sha256"
        "encoding/base64"
        "fmt"
      )
   
      func verifyWebhook(body string, secret string) string {
        key := []byte(secret)
        h := hmac.New(sha256.New, key)
        h.Write([]byte(body))
        return base64.StdEncoding.EncodeToString(h.Sum(nil))
      }
   
      func main() {
        fmt.Println(verifyWebhook("INSERT-YOUR-JSON-BODY-HERE", "INSERT-YOUR-CLIENT-SECRET-HERE"))
      }
       
      ```
  
      **Node JS**
  
      Prerequisite: install the [crypto-js](https://www.npmjs.com/package/crypto-js) dependency.
  
      ```
      //NodeJS
   
      const HMACSHA256 = require("crypto-js/hmac-sha256");
      const BASE64 = require('crypto-js/enc-base64');
   
      const calculatedHmac = BASE64.stringify(HMACSHA256('INSERT-YOUR-JSON-BODY-HERE', 'INSERT-YOUR-CLIENT-SECRET-HERE'));
      console.log(calculatedHmac);
      ```


      **C#**
  
      ```
      using System;
      using System.Text;
      using System.Collections.Generic;
      using System.Linq;
      using System.IO;
      using System.Text.RegularExpressions;
      using System.Security.Cryptography;
      
      namespace WebhookTester
      {
          public class Program
          {
              public static void Main(string[] args)
              {
                  //Your code goes here
                  string clientSecret = "INSERT-YOUR-CLIENT-SECRET-HERE";
                  string jsonBody = "INSERT-YOUR-JSON-BODY-HERE";
                  string calculatedHmac = calculateHmac(jsonBody, clientSecret);
                  Console.WriteLine(verifyWebhook(calculatedHmac,"HMAC-HEADER-RECEIVED-IN-REQUEST"));
                  
              }
              
              static string calculateHmac(string jsonBody, string clientSecret)
              {
                  Encoding encoding = Encoding.UTF8;
                  var key = encoding.GetBytes(clientSecret);
                  HMACSHA256 hmacsha256 = new HMACSHA256(key);
                  var byteArray = encoding.GetBytes(jsonBody);
                  var result = hmacsha256.ComputeHash(byteArray);
                  return Convert.ToBase64String(result);    
              }
              
              static bool verifyWebhook(string calculatedHmac, string hmacHeader)
              {
                return calculatedHmac.Equals(hmacHeader);
              }
          }
      
      }
      ```


      **Java** 

      Using library [Apache Common Codec](https://commons.apache.org/proper/commons-codec/)
  
      ```
        
      import java.security.InvalidKeyException;
      import java.security.NoSuchAlgorithmException;
      import javax.crypto.Mac;
      import javax.crypto.spec.SecretKeySpec;
      import org.apache.commons.codec.binary.Base64;


      public class MyClass {
          public static String calculateHmac(String message, String secret) throws InvalidKeyException, NoSuchAlgorithmException {
              String algorithm = "HmacSHA256";
              Mac sha256_HMAC = Mac.getInstance(algorithm);
              SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), algorithm);
              sha256_HMAC.init(secret_key);
              return Base64.encodeBase64String(sha256_HMAC.doFinal(message.getBytes()));
          }

          public static Boolean verifyWebhook(String calculatedHmac, String hmacHeader)
          {
              return calculatedHmac.equals(hmacHeader);
          }

          public static void main(String[] args) {
              String clientSecret = "INSERT-YOUR-CLIENT-SECRET-HERE";
              String jsonBody = "INSERT-YOUR-JSON-BODY-HERE";
              try {
                  String calculatedHmac = calculateHmac(jsonBody, clientSecret);
                  System.out.println(verifyWebhook(calculatedHmac,"HMAC-HEADER-RECEIVED-IN-REQUEST" ));
              } catch (Exception e) {
                  System.out.println("Error: " + e);
              }
          }
      }
       
      ```

  
      # Respond to a webhook
      To acknowledge receipt of a webhook, your endpoint must return an HTTP `200 OK` status code. Any response outside of
      the `2xx` range suggests to Ninja Van that you didn't receive your webhook. To this end, Ninja Van
      implemented a 5-second timeout period and a retry period for subscriptions.
  
      You should monitor your application for failing webhooks. If you're receiving a Ninja Van webhook,
      the most important thing to do is respond quickly.
      
      We recommend that apps defer processing until after the response has been sent. To ensure that
      webhooks are not sent repeatedly even after you received them, respond with a `200 OK`
      status code before carrying on with any further in-house webhook processing.

      **Note:** If we notice that too many webhooks are rejected or not acknowledged, 
      you will get blocked and stop receiving webhooks from us. So make sure to monitor
      your application for failing webhooks. As soon as you can acknowledge our webhooks with
      a `200 OK` status code, you will continue receiving webhooks for subsequent events.

      # Configure a webhook

      The trouble with testing your webhooks through the API is that you need a publicly visible URL to
      handle them. Unlike client-side redirects, webhooks originate directly from the server. This means
      that you can't use the following as an endpoint in your testing environment:
      * Localhost
      * Any URL ending with the word "internal" (such as thisshop.com/internal)
      * Fictitious domains, such as www.example.com
      
      Fortunately, there are a couple of tools that make working with webhooks during development
      much easier. Among them RequestBin, Pagekite, and ngrok.

      # What if everything blows up?

      Say, your hosting center has exploded and your app has been offline for a long time. Don't worry—Ninja Van keeps sending webhooks at an exponentially increasing
      time interval for a particular event, 48 hours after the event occurrence time. After this 48-hour time
      window, we'll drop the webhook. Ensure that your services have the capacity to handle all
      incoming webhooks as and when you go online.

  - name: Reference
    description: |+
      # FAQ
      ## OAuth API
      ### 1. Where can I find the Client ID and Client Secret?
      The Client ID and Client Secret are available in your Ninja Dashboard account. If you don't have access
      to Dashboard, contact your Account Manager.

      ### 2. Can I use the same Client ID and Client Secret for sandbox and production?
      No, because Ninja Dashboards in the sandbox and production environments are different, and they use different credentials. 
      To obtain a Client ID and Client Secret for production, log in to your production Dashboard, same way as you would do for sandbox. 
      If you don't have access to Dashboard in production, contact your Account Manager.
      
      ### 3. Should I regenerate the Client ID and Client Secret in my Ninja Dashboard account after I get access to production?
      You don't need to regenerate the Client ID and Client Secret. But make sure to regenerate your token
      after you're granted access to the production environment.

      ### 4. I just received integration audit approval but I'm still encountering authentication issues. Why?
      You need to generate new access tokens for production after you've got the audit approval. See [Get access to production](#section/Get-started/Get-access-to-production).

      ## Order API
      ### 1. Can I submit orders for my integration audit using Postman or any API testing tool?
      No. The purpose of an integration audit is to verify that your integrated system meets the necessary requirements. So 
      we expect shippers to submit orders via an integrated workflow that uses our APIs.
      
      ### 2. What are the prerequisites for testing the APIs?
      To test the APIs, you need to have the following:
      1. Ninja Van Postpaid Pro account.
      2. Access to Ninja Dashboard.

      ### 3. I'm trying to create an order via the API, but I received a 403 status code. How do I resolve it?
      If the Order API returns an HTTP `403` status code, then you don't have the required permissions to access the API. To troubleshoot, check the following:

      * **Sandbox:** Check that you're sending requests to the sandbox URL with the country code `sg`: `https://api-sandbox.ninjavan.co/sg`. This is because Ninja Van only supports the Singapore sandbox API.
      * **Production:** You won't have access to any API in the production environment until our developer support team reviews your integration. If you passed the integration audit and the API still returns a `403` status code, then verify that you're using the correct URL with the country code: `https://api.ninjavan.co/{countryCode}/4.1/orders`.

      ## Webhooks
      ### 1. Can I test webhooks in the sandbox environment?
      The only webhooks that can be tested in the sandbox environment are Pending Pickup
      and Cancelled webhooks. All the other webhooks require operational activities that aren't feasible in sandbox.

      ### 2. I create orders via Ninja Dashboard and want to subscribe to webhooks. Do I need to complete an integration audit?
      No. An integration audit is required only if you need access to our production APIs. As a Dashboard user, you need to first generate a Client ID and Client Secret, 
      and then directly subscribe to the webhooks and receive updates for your orders.

      # Errors
      ## Error glossary

      Ninja Van APIs return standard HTTP status codes. For errors, we also include extra information about what went wrong encoded in the response as JSON. The various HTTP status codes we may return are listed in this section.

      | Status code | Description |
      |-------|-------------------|
      | `400` | **Bad Request**<br>Your request couldn't be processed.<br><br>**Invalid Data**<br>Your request failed our validation.<br><br>These status codes generally contain a descriptive message, which helps you troublehsoot the error. See [Common error messages](#section/Errors/Common-error-messages). |
      | `401` | **Unauthorized**<br>The access token is either invalid or expired. For more information about access tokens, see [Authentication](#section/Introduction/Authentication). |
      | `403` | **Forbidden**<br>You don't have the required permissions to access the API. Following are the possible causes for this error.<br><br>- You're not using the correct sandbox URL. Ninja Van only supports the Singapore sandbox API, so all requests must be sent to the country code `sg`: `https://api-sandbox.ninjavan.co/sg`.<br><br>- You haven't passed the integration audit. You won't have access to any API in the production environment until our developer support team reviews your integration. If you passed the integration audit and the API still returns a `403` status code, then verify that you're using the correct URL with the country code: `https://api.ninjavan.co/{countryCode}/4.1/orders`.<br><br>- You're trying to access the waybill generation endpoint without a prior request. For more information, see [How to request access](#tag/Order-API/paths/~1{countryCode}~12.0~1reports~1waybill/get).<br><br>**The shipper's account is frozen and cannot be used.**<br>Your account has been frozen due to outstanding fees. To unfreeze the account, clear all due fees and contact your Account Manager for assistance. |
      | `429` | **API rate limit exceeded**<br>You've exceeded the request limit and got temporarily blocked from accessing the API. The block will be removed after a few hours, after which you can access the API again. |
      | `5xx` | **Internal Server Error**<br>These status codes are rare, and may happen for a small percentage of orders during peak periods. If the API returns a `5xx` status code, retry your request after some time. We encourage you to have a retry mechanism in place. |

      ## Common error messages
      This section describes common error messages for HTTP `400` status code returned by our APIs. If the error that you encountered isn't mentioned, try to resolve it based on the information in the response. If you still need help, send an email with the error details to [developer support](#section/Introduction/Contact-information).

      ### Order API
      #### Service level not supported
      
      **Error message:** Service Level is not supported for this shipper account.

      **Cause:** The service is not enabled in your account.

      **Suggested action:** To enable the service, contact your Account Manager.
      
      **Sample error response**
      ```
      {
        "error":
          {
            "request_id": "1ba6da4f-0709-416e-9e30-a5546130b4d2",
            "title": "Invalid Data",
            "message": "Please check your request payload for validation errors.",
            "details":[
              {
                "reason": "Validation Error",
                "field": "null",
                "message": "Service Level is not supported for this shipper account."
              }
            ]
          }
      }
      ```

      #### UTF extended charset not supported

      **Error code:** 127014
      
      **Error message:** Invalid charset! UTF Extended charset is not supported

      **Cause:** The request payload contains special characters that include symbols, emojis, and so on.

      **Suggested action:** Remove these characters and try again.
      
      **Sample error response**

      ```
      {
        "error":
          {
            "code": "127014",
            "title": "Bad Request",
            "message": "Invalid charset! UTF Extended charset is not supported"
          }
      }
      ```

      #### Duplicate tracking ID

      **Error code:** 109201
      
      **Error message:** Tracking ID (123456789) has been used, please use a different tracking ID and try again

      **Cause:** You already created an order with this tracking ID.

      **Suggested action:** Use a unique `requested_tracking_number` for each order.
      
      **Sample error response**

      ```
      {
        "error": {
          "code": 109201,
          "request_id": "1ba6da4f-0709-416e-9e30-a5546130b4d2",
          "title": "Duplicate Tracking ID",
          "message": "Tracking ID (123456789) has been used, please use a different tracking ID and try again"
          }
      }
      ```

      #### Invalid pickup or delivery time slot
      
      **Error message:** Invalid timeslot provided. Please check the API docs for a list of valid time slots.

      **Cause:** You provided an invalid pickup or delivery time slot.

      **Suggested action:** Specify valid time slots in `parcel_job.pickup_timeslot` and `parcel_job.delivery_timeslot`, and try again.
      
      **Sample error response**

      ```
      {
        "error": {
          "request_id": "1ba6da4f-0709-416e-9e30-a5546130b4d2",
          "title": "Invalid Data",
          "message": "Please check your request payload for validation errors.",
          "details": [
          {
            "reason": "Validation Error",
            "message": "Invalid timeslot provided. Please check the API docs for a list of valid time slots."
          }
          ]
        }
      }
      ```
      
      #### Contact information not provided
      
      **Error message:** Invalid contact information. Please provide either a 'email' or 'phone_number'.

      **Cause:** You didn't provide any contact information.

      **Suggested action:** Specify either the `email` or `phone_number`, and try again.
      
      **Sample error response**

      ```
      {
        "error": {
          "request_id": "1ba6da4f-0709-416e-9e30-a5546130b4d2",
          "title": "Invalid Data",
          "message": "Please check your request payload for validation errors.",
          "details": [
            {
              "reason": "Validation Error",
              "field": null,
              "message": "'Invalid contact information. Please provide either a 'email' or 'phone_number'."
            }
          ]
        }
      }
      ```

      #### Dimension information not provided
      
      **Error message:** At least a form of dimension must be provided. Please check with your shipper support team to see which one you should be providing.

      **Cause:** You didn't provide any dimensions.

      **Suggested action:** Specify at least one of following dimensions.
      1. Size: `parcel_job.dimensions.size`
      2. Weight: `parcel_job.dimensions.weight`
      3. Length, width, and height: `parcel_job.dimensions.length`, `parcel_job.dimensions.width`, and `parcel_job.dimensions.height`.
      
      **Sample error response**

      ```
      {
        "error": {
        "request_id": "1ba6da4f-0709-416e-9e30-a5546130b4d2",
        "title": "Invalid Data",
        "message": "Please check your request payload for validation errors.",
        "details": [
          {
            "reason": "Validation Error",
            "message": "At least a form of dimension must be provided. Please check with your shipper support team to see which one you should be providing.",
            "field": "parcel_job.dimensions"
          }
        ]
        }
      }
      ```

      #### Invalid requested tracking prefix
      
      **Error message:** The 'requested_tracking_number' field must start with the correct prefix.

      **Cause:** The `requested_tracking_number` field doesn't contain the prefix configured for your account.

      **Suggested action:** Check with your Account Manager on the prefix configured, and use the right prefix.
      
      **Sample error response**

      ```
      {
        "error": {
        "request_id": "1ba6da4f-0709-416e-9e30-a5546130b4d2",
        "title": "Invalid Data",
        "message": "Please check your request payload for validation errors.",
        "details": [
          {
            "reason": "Validation Error",
            "field": null,
            "message": "The 'requested_tracking_number' field must start with the correct prefix."
          }
          ]
        }
      }
      ```

      #### Invalid requested tracking number
      
      **Error message:** The 'requested_tracking_number' field must be between 1 and 9 characters (please do not pass a prefix), and can contain alphanumeric characters, and the (-) dash symbol.

      **Cause:** You provided the wrong format for the requested_tracking_number field.

      **Suggested action:** Check whether you are using the correct format, and try again.
      
      **Sample error response**

      ```
      {
        "error": {
        "request_id": "1ba6da4f-0709-416e-9e30-a5546130b4d2",
        "title": "Invalid Data",
        "message": "Please check your request payload for validation errors.",
        "details": [
          {
            "reason": "Validation Error",
            "field": null,
            "message": "The 'requested_tracking_number' field must be between 1 and 9 characters (please do not pass a prefix), and can contain alphanumeric characters, and the (-) dash symbol."
          }
          ]
        }
      }
      ```

      # Sample order creation requests
      This section provides sample order creation payloads for pre-determined business scenarios.
      ## Standard parcels
      In this scenario, an e-commerce platform sends Standard parcels.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | Parcel | For sending orders, the corresponding order type is `Parcel`. |
        | service_level | Standard | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Parcel`. |
        

      ```
      {
        "service_type": "Parcel",
        "service_level": "Standard",
        "requested_tracking_number": "1234-56789",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+6563337193",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+6502700553",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "parcel_job":{
            "is_pickup_required":false,
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_instructions": "Please be careful with the v-day flowers.",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        }
      }
      ```


      ## Standard parcels with a pickup
      In this scenario, an e-commerce platform sends Standard parcels that require a pickup.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Parcel` | For sending orders, the corresponding order type is `Parcel`. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Parcel`. |
        | parcel_job.is_pickup_required | BOOLEAN | If you require a pickup, then set this to `true`. |
        | parcel_job.pickup_address_id | STRING | Optional, but when provided, Ninja Van syncs your address book for easier pickup management. |
        

      ```  
      {
        "service_type": "Parcel",
        "service_level": "Standard",
        "requested_tracking_number": "1234-56789",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+6563337193",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+6502700553",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "parcel_job":{
            "is_pickup_required":true,
            "pickup_service_type": "Scheduled",
            "pickup_service_level": "Standard",
            "pickup_address_id": "123456",
            "pickup_date": "2021-12-15",
            "pickup_timeslot":{
              "start_time": "09:00",
              "end_time": "18:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_instructions": "Please be careful with as the packages are heavy",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        }
      }
      ```

      
      ## Standard parcels with cash on delivery
      In this scenario, an e-commerce platform sends Standard parcels with the payment term "cash on delivery" (COD).

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Parcel` | For sending orders, the corresponding order type is `Parcel`. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Parcel`. |
        | parcel_job.cash_on_delivery | DECIMAL | This is the cash amount that Ninja Van's drivers and riders will collect from the recipient upon delivery. |


      ```
      {
        "service_type": "Parcel",
        "service_level": "Standard",
        "requested_tracking_number": "1234-56789",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+6563337193",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+6502700553",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "parcel_job":{
            "cash_on_delivery":200.00,
            "pickup_date": "2021-12-15",
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_instructions": "Please be careful with as the packages are heavy",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        }
      }
      
      ```

      
      ## Standard parcels dropped off at a Ninja Point
      In this scenario, an e-commerce platform sends Standard parcels that will be dropped off at a Ninja Point.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Parcel` | For sending orders, the corresponding order type is `Parcel`. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Parcel`. |
        | parcel_job.is_pickup_required | BOOLEAN | Either set this to `false`, or undefined. |
        
      ```
      
      {
        "service_type": "Parcel",
        "service_level": "Standard",
        "requested_tracking_number": "1234-56789",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+6563337193",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+6502700553",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "parcel_job":{
            "is_pickup_required":false,
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_instructions": "Please be careful with as the packages are heavy",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        }
      }
      
      ```

      
      ## Return parcels to a seller's warehouse with a pickup
      In this scenario, a customer of an e-commerce platform returns a parcel to a seller's warehouse, with a pickup required.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Return` | For returning parcels, the corresponding order type is `Return`. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Return`. |
        | parcel_job.is_pickup_required | BOOLEAN | If you require a pickup, then set this to `true`. |

      ```
      
      {
        "service_type": "Return",
        "service_level": "Standard",
        "requested_tracking_number": "1234-56789",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+6563337193",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "30 Jalan Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+6502700553",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "28 Jalan Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159362"
            }
        },
        "parcel_job":{
            "is_pickup_required":true,
            "pickup_service_type": "Scheduled",
            "pickup_service_level": "Standard",
            "pickup_address":{
              "name": "John Doe",
              "phone_number": "+6563337193",
              "email": "john.doe@gmail.com",
              "address":{
                  "address1": "30 Jalan Kilang Barat",
                  "address2": "",
                  "country": "SG",
                  "postcode": "159363"
              }
            },
            "pickup_address_id": "98989012",
            "pickup_date": "2021-12-15",
            "pickup_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Singapore"
            },
            "pickup_approximate_volume": "Half-Van Load",
            "pickup_instruction": "Please be careful, the package is fragile",
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_instructions": "Please be careful with the v-day flowers.",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        }
      }
      
      ```

      ## Return parcels to a seller's warehouse but dropped off at a Ninja Point
      In this scenario, a customer of an e-commerce platform returns a parcel to a seller's warehouse by dropping it off at a Ninja Point.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Return` | For returning parcels, the corresponding order type is `Return`. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Return`. |
        | parcel_job.is_pickup_required | BOOLEAN | Either set this to `false`, or undefined. |
      
      ```
      
      {
        "service_type": "Return",
        "service_level": "Standard",
        "requested_tracking_number": "1234-56789",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+6563337193",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+6502700553",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "parcel_job":{
            "is_pickup_required":false,
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_instructions": "Please be careful with as the packages are heavy",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        }
      }
      
      ```

      
      ## Express parcels
      In this scenario, an e-commerce platform sends Express parcels.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Parcel` | For sending orders, the corresponding order type is `Parcel`. |
        | service_level | `Express` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Parcel`. |
        
      ```
      
      {
        "service_type": "Parcel",
        "service_level": "Express",
        "requested_tracking_number": "1234-56789",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+60138201527",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "17 Lorong Jambu 3",
              "address2": "",
              "area": "Taman Sri Delima",
              "city": "Simpang Ampat",
              "state": "Pulau Pinang",
              "country": "MY",
              "postcode": "51200"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+60103067174",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "Jalan PJU 8/8",
              "address2": "",
              "area": "Damansara Perdana",
              "city": "Petaling Jaya",
              "state": "Selangor",
              "country": "MY",
              "postcode": "47820"
            }
        },
        "parcel_job":{
            "is_pickup_required":false,
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Kuala_Lumpur"
            },
            "delivery_instructions": "Please be careful with the v-day flowers.",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        }
      }
      
      ```

      
      ## Standard Marketplace parcels
      In this scenario, an e-commerce platform sends Standard Marketplace parcels.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Marketplace` | Marketplaces require special *marketplace* data, so the order type must be `Marketplace`. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Marketplace`. |
        | marketplace | JSON | Required because the order type is `Marketplace`. |

      ```
      
      {
        "service_type": "Marketplace",
        "service_level": "Standard",
        "requested_tracking_number": "1234-56789",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+60138201527",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "17 Lorong Jambu 3",
              "address2": "",
              "area": "Taman Sri Delima",
              "city": "Simpang Ampat",
              "state": "Pulau Pinang",
              "country": "MY",
              "postcode": "51200"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+60103067174",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "Jalan PJU 8/8",
              "address2": "",
              "area": "Damansara Perdana",
              "city": "Petaling Jaya",
              "state": "Selangor",
              "country": "MY",
              "postcode": "47820"
            }
        },
        "parcel_job":{
            "is_pickup_required":false,
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Kuala_Lumpur"
            },
            "delivery_instructions": "Please be careful with the v-day flowers.",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        },
        "marketplace":{
            "seller_id": "4567",
            "seller_company_name": "FlowersRUs"
        }
      }
      
      ```

      
      ## Standard Marketplace parcels with a pickup
      In this scenario, an e-commerce platform sends Standard Marketplace parcels that require a pickup.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Marketplace` | Marketplaces require special *marketplace* data, so the order type must be `Marketplace`. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Marketplace`. |
        | marketplace | JSON | Required because the order type is `Marketplace`. |
        | parcel_job.pickup_address_id | STRING | Optional, but when provided, Ninja Van syncs your address book for easier pickup management. |

      ```
      
      {
        "service_type": "Marketplace",
        "service_level": "Standard",
        "requested_tracking_number": "NV-123456",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+6563337193",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+6502700553",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "28 Jln Kilang Barat",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "parcel_job":{
            "is_pickup_required":true,
            "pickup_service_type": "Scheduled",
            "pickup_service_level": "Standard",
            "pickup_address_id": "123456",
            "pickup_date": "2021-12-15",
            "pickup_timeslot":{
              "start_time": "09:00",
              "end_time": "18:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_instructions": "Please be careful with as the packages are heavy",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        },
        "marketplace":{
            "seller_id": "4567",
            "seller_company_name": "Ninja Van Logistics"
        }
      }
      
      ```

      
      ## Standard Marketplace parcels with a Premium pickup
      In this scenario, an e-commerce platform sends Standard Marketplace parcels that require a Premium level pickup. 

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Marketplace` | Marketplaces require special *marketplace* data, so the order type must be `Marketplace`. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Marketplace`. |
        | marketplace | JSON | Required because the order type is `Marketplace`. |
        | parcel_job.pickup_service_type | `Scheduled` | For indicating that you require a scheduled pickup. |
        | parcel_job.pickup_service_level | `Premium` | For indicating that your scheduled pickup is a Premium level pickup. |
        | parcel_job.pickup_address_id | STRING | Optional, but when provided, Ninja Van syncs your address book for easier pickup management. |

      ```
      
      {
          "service_type": "Marketplace",
          "service_level": "Standard",
          "requested_tracking_number": "NV-123456",
          "reference": {
              "merchant_order_number": "ship-123"
          },
          "from": {
              "name": "John Doe",
              "phone_number": "+6563337193",
              "email": "john.doe@gmail.com",
              "address": {
                  "address1": "30 Jln Kilang Barat",
                  "country": "SG",
                  "postcode": "159363"
              }
          },
          "to": {
              "name": "Jane Doe",
              "phone_number": "+6502700553",
              "email": "jane.doe@gmail.com",
              "address": {
                  "address1": "28 Jln Kilang Barat",
                  "country": "SG",
                  "postcode": "159363"
              }
          },
          "parcel_job": {
              "is_pickup_required": true,
              "pickup_service_type": "Scheduled",
              "pickup_service_level": "Premium",
              "pickup_address_id": "123456",       
              "pickup_date": "2021-12-15",
              "pickup_timeslot": {
                  "start_time": "09:00",
                  "end_time": "12:30",
                  "timezone": "Asia/Singapore"
              },
              "pickup_instructions": "Parcels will be ready by 9 AM, and I'm leaving at 10:31 AM.",
              "delivery_start_date": "2021-12-16",
              "delivery_timeslot": {
                  "start_time": "09:00",
                  "end_time": "22:00",
                  "timezone": "Asia/Singapore"
              },
              "delivery_instructions": "Please be careful with as the packages are heavy",
              "dimensions": {
                  "weight": 1.0,
                  "size": "L"
              }
          },
          "marketplace": {
              "seller_id": "4567",
              "seller_company_name": "Ninja Van Logistics"
          }
      }
      
      ```


      ## Standard Marketplace parcels self-collected at a Ninja Point
      In this scenario, an e-commerce platform sends Standard Marketplace parcels that will be self-collected by recipients at a Ninja Point.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Marketplace` | Marketplaces require special *marketplace* data, so the order type must be `Marketplace`. |
        | service_level | `Standard` | Used for SLA calculations. Adjust the required service level accordingly. |
        | parcel_job | JSON | Required because the order type is `Marketplace`. |
        | parcel_job.allow_self_collection | BOOLEAN | If you allow self-collection, then set this to `true`. |
        | to.collection_point | STRING | Required if you allow self-collection. This must contain the short name of the Ninja Point. |

      ```
      
      {
          "service_type": "Marketplace",
          "service_level": "Standard",
          "requested_tracking_number": "1234-56789",
          "reference": {
              "merchant_order_number": "ship-123"
          },
          "from": {
              "name": "John Doe",
              "phone_number": "+6563337193",
              "email": "john.doe@gmail.com",
              "address": {
                  "address1": "30 Jln Kilang Barat",
                  "address2": "",
                  "country": "SG",
                  "postcode": "159363"
              }
          },
          "to": {
              "name": "Jane Doe",
              "phone_number": "+6502700553",
              "email": "jane.doe@gmail.com",
              "address": {
                  "address1": "181 Orchard Rd",
                  "address2": "",
                  "country": "SG",
                  "postcode": "238896"
              },
              "collection_point": "Ninja PUDO @ Orchard Road"
          },
          "parcel_job": {
              "is_pickup_required": false,
              "allow_self_collection": true,
              "delivery_start_date": "2021-12-16",
              "delivery_timeslot": {
                  "start_time": "09:00",
                  "end_time": "22:00",
                  "timezone": "Asia/Singapore"
              },
              "delivery_instructions": "Please be careful with the v-day flowers.",
              "dimensions": {
                  "weight": 0.5,
                  "size": "S"
              }
          },
          "marketplace": {
              "seller_id": "4567",
              "seller_company_name": "Ninja Van Logistics"
          }        
      }
      
      ```    

      ## Standard Marketplace parcels where a shipper drops-off a parcel at a Ninja Point
      In this scenario, an e-commerce platform sends Standard Marketplace parcels, where the parcels are dropped off at a Ninja Point.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Marketplace` | Marketplaces require special *marketplace* data, so the order type must be `Marketplace`. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `Marketplace`. |
        | parcel_job.is_pickup_required | BOOLEAN | Either set this to `false`, or undefined. |
        
      ```
      
      {
        "service_type": "Marketplace",
        "service_level": "Standard",
        "requested_tracking_number": "1234-56789",
        "reference":{
            "merchant_order_number": "ship-123"
        },
        "from":{
            "name": "John Doe",
            "phone_number": "+6563337193",
            "email": "john.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "to":{
            "name": "Jane Doe",
            "phone_number": "+6502700553",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "30 Jln Kilang Barat",
              "address2": "",
              "country": "SG",
              "postcode": "159363"
            }
        },
        "parcel_job":{
            "is_pickup_required":false,
            "delivery_start_date": "2021-12-16",
            "delivery_timeslot":{
              "start_time": "09:00",
              "end_time": "22:00",
              "timezone": "Asia/Singapore"
            },
            "delivery_instructions": "Please be careful with as the packages are heavy",
            "dimensions":{
              "weight":1.0,
              "size": "L"
            }
        }
      }
      
      ```    

      
      ## International parcels imported by a forwarder
      In this scenario, an e-commerce platform sends International parcels that were imported by a forwarder.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `International` | For sending parcels that were imported. |
        | service_level | `Standard` | Used for SLA calculations. |
        | parcel_job | JSON | Required because the order type is `International`. |
        | international | JSON | Required because the order type is `International`. |
        | international.portation | `Import` | For indicating that the parcel was imported by a forwarder. This determines how the parcel is returned to the shipper if the recipient rejects it. |

      ```
          
      {
          "service_type": "International",
          "service_level": "Standard",
          "requested_tracking_number": "1234-56789",
          "reference": {
              "merchant_order_number": "ship-123"
          },
          "from": {
              "name": "Jane Doe",
              "phone_number": "+60103067174",
              "email": "jane.doe@gmail.com",
              "address":{
                "address1": "Jalan PJU 8/8",
                "address2": "",
                "area": "Damansara Perdana",
                "city": "Petaling Jaya",
                "state": "Selangor",
                "country": "MY",
                "postcode": "47820"
              }
          },
          "to": {
              "name": "John Doe",
              "phone_number": "+6563337193",
              "email": "john.doe@gmail.com",
              "address": {
                  "address1": "30 Jln Kilang Barat",
                  "address2": "",
                  "country": "SG",
                  "postcode": "159363"
              }
          },
          "parcel_job": {
              "is_pickup_required": false,
              "delivery_start_date": "2021-12-16",
              "delivery_timeslot": {
                  "start_time": "09:00",
                  "end_time": "22:00",
                  "timezone": "Asia/Singapore"
              },
              "delivery_instructions": "Please be careful with the v-day flowers.",
              "dimensions": {
                  "weight": 1.0,
                  "size": "L"
              }
          },
          "international": {
              "portation": "Import"
          },
          "experimental_customs_declaration":{
              "customs_description": "men’s shirts",
              "native_customs_description": "男人衬衫",
              "goods_currency": "USD",
              "goods_value": "15.99",
              "battery_type": "No Battery",
              "battery_packing": "No Battery",
              "hs_code": "543111",
              "trade_terms": "DDU",
              "invoice_url": "www.invoice.com/12345.pdf"
          }        
      }
            
      ```

      ## International parcels exported by Ninja Van and imported into another country
      In this scenario, an e-commerce platform sends International parcels that are to be exported out by Ninja Van and imported into another country for last-mile delivery.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `International` | For sending parcels that are to be exported. |
        | service_level | `Standard` | Used for SLA calculations. Adjust the required service level accordingly. |
        | parcel_job | JSON | Required because the order type is `International`. |
        | international | JSON | Required because the order type is `International`. |
        | international.portation | `Export` | For indicating that the parcel needs to be exported to another country for last-mile delivery. This determines how the parcel is returned to the shipper if the recipient rejects it. |

      ```
      
      {
          "service_type": "International",
          "service_level": "Standard",
          "requested_tracking_number": "1234-56789",
          "reference": {
              "merchant_order_number": "ship-123"
          },
          "from": {
              "name": "John Doe",
              "phone_number": "+6563337193",
              "email": "john.doe@gmail.com",
              "address": {
                  "address1": "30 Jln Kilang Barat",
                  "address2": "",
                  "country": "SG",
                  "postcode": "159363"
              }
          },
          "to":{
            "name": "Jane Doe",
            "phone_number": "+60103067174",
            "email": "jane.doe@gmail.com",
            "address":{
              "address1": "Jalan PJU 8/8",
              "address2": "",
              "area": "Damansara Perdana",
              "city": "Petaling Jaya",
              "state": "Selangor",
              "country": "MY",
              "postcode": "47820"
            }
          },
          "parcel_job": {
              "is_pickup_required": false,
              "delivery_start_date": "2021-12-16",
              "delivery_timeslot": {
                  "start_time": "09:00",
                  "end_time": "22:00",
                  "timezone": "Asia/Singapore"
              },
              "delivery_instructions": "Please be careful with the v-day flowers.",
              "dimensions": {
                  "weight": 1.0,
                  "size": "L"
              }
          },
          "international": {
              "portation": "Export"
          },
          "experimental_customs_declaration":{
              "customs_description": "men’s shirts",
              "native_customs_description": "男人衬衫",
              "goods_currency": "USD",
              "goods_value": "15.99",
              "battery_type": "No Battery",
              "battery_packing": "No Battery",
              "hs_code": "543111",
              "trade_terms": "DDU",
              "invoice_url": "www.invoice.com/12345.pdf"
          }
      }
      
      ```

      
      ## Standard parcels self-collected at a Ninja Point
      In this scenario, an e-commerce platform sends Standard parcels that will be self-collected by recipients at a Ninja Point.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Parcel` | For sending orders, the corresponding order type is `Parcel`. |
        | service_level | `Standard` | Used for SLA calculations. Adjust the required service level accordingly. |
        | parcel_job | JSON | Required because the order type is `Parcel`. |
        | parcel_job.allow_self_collection | BOOLEAN | If you allow self-collection, then set this to `true`. |
        | to.collection_point | STRING | Required if you allow self-collection. This must contain the short name of the Ninja Point. |

      ```
      
      {
          "service_type": "Parcel",
          "service_level": "Standard",
          "requested_tracking_number": "1234-56789",
          "reference": {
              "merchant_order_number": "ship-123"
          },
          "from": {
              "name": "John Doe",
              "phone_number": "+6563337193",
              "email": "john.doe@gmail.com",
              "address": {
                  "address1": "30 Jln Kilang Barat",
                  "address2": "",
                  "country": "SG",
                  "postcode": "159363"
              }
          },
          "to": {
              "name": "Jane Doe",
              "phone_number": "+6502700553",
              "email": "jane.doe@gmail.com",
              "address": {
                  "address1": "181 Orchard Rd",
                  "address2": "",
                  "country": "SG",
                  "postcode": "238896"
              },
              "collection_point": "Ninja PUDO @ Orchard Road"
          },
          "parcel_job": {
              "is_pickup_required": false,
              "allow_self_collection": true,
              "delivery_start_date": "2021-12-16",
              "delivery_timeslot": {
                  "start_time": "09:00",
                  "end_time": "22:00",
                  "timezone": "Asia/Singapore"
              },
              "delivery_instructions": "Please be careful with the v-day flowers.",
              "dimensions": {
                  "weight": 0.5,
                  "size": "S"
              }
          }
      }
      
      ```

      
      ## Corporate HQ sending Standard parcels
      In this scenario, a corporate HQ sends Standard parcels.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Parcel` | For sending parcels under the corporate HQ's account. |
        | service_level | `Standard` | Used for SLA calculations. Adjust the required service level accordingly. |
        | parcel_job | JSON | Required because the order type is `Parcel`. |
        | parcel_job.cash_on_delivery | DECIMAL | For cash on delivery parcels, this is the cash amount that Ninja Van's drivers and riders will collect from the recipient upon delivery. |
        | parcel_job.is_pickup_required | BOOLEAN | If you require a pickup, then set this to `true`. |
        | parcel_job.pickup_address_id | STRING | Optional, but when provided, Ninja Van syncs your address book for easier pickup management. |      

      ```
      
      {
          "service_type": "Parcel",
          "service_level": "Standard",
          "requested_tracking_number": "1234-56789",
          "reference": {
              "merchant_order_number": "ship-123"
          },
          "from": {
              "name": "John Doe",
              "phone_number": "+6563337193",
              "email": "john.doe@gmail.com",
              "address": {
                  "address1": "30 Jln Kilang Barat",
                  "address2": "",
                  "country": "SG",
                  "postcode": "159363"
              }
          },
          "to": {
              "name": "Jane Doe",
              "phone_number": "+6502700553",
              "email": "jane.doe@gmail.com",
              "address": {
                  "address1": "69 Lorong 6 Geylang",
                  "address2": "",
                  "country": "SG",
                  "postcode": "399221"
              }
          },
          "parcel_job": {
              "is_pickup_required": false,
              "cash_on_delivery": 200.00,
              "allow_self_collection": true,
              "delivery_start_date": "2021-12-16",
              "delivery_timeslot": {
                  "start_time": "09:00",
                  "end_time": "22:00",
                  "timezone": "Asia/Singapore"
              },
              "delivery_instructions": "Please be careful with the v-day flowers.",
              "dimensions": {
                  "weight": 0.5,
                  "size": "S"
              }
          }
      }
      
      ```

      ## Corporate HQ sending Standard parcels for branches with a Premium pickup
      In this scenario, a corporate HQ sends Standard parcels for their branches with a Premium pickup required.

        | Parameter | Value | Description |
        |-------------|----------|----------|
        | service_type  | `Corporate` | For sending parcels under the corporate HQ's account. |
        | service_level | `Standard` | Used for SLA calculations. |
        | corporate | JSON | Required because the order type is `Corporate`. |  
        | corporate.branch_id | STRING | Specify the corporate branch ID that this order belongs to. |  
        | parcel_job | JSON | Required because the order type is `Corporate`. |
        | parcel_job.is_pickup_required | BOOLEAN | If you require a pickup, then set this to `true`. |
        | parcel_job.pickup_service_type | Scheduled | For indicating that you require a scheduled pickup. |
        | parcel_job.pickup_service_level | Premium | For indicating that your scheduled pickup is a Premium level pickup. |
        | parcel_job.pickup_address_id | STRING | Optional, but when provided, Ninja Van syncs your address book for easier pickup management. |            
        

      ```
      
      {
          "service_type": "Corporate",
          "service_level": "Standard",
          "requested_tracking_number": "NV-123456",
          "reference": {
              "merchant_order_number": "ship-123"
          },
          "from": {
              "name": "John Doe",
              "phone_number": "+6563337193",
              "email": "john.doe@gmail.com",
              "address": {
                  "address1": "30 Jln Kilang Barat",
                  "country": "SG",
                  "postcode": "159363"
              }
          },
          "to": {
              "name": "Jane Doe",
              "phone_number": "+6502700553",
              "email": "jane.doe@gmail.com",
              "address": {
                  "address1": "28 Jln Kilang Barat",
                  "country": "SG",
                  "postcode": "159363"
              }
          },
          "corporate":
          {
              "branch_id": "1234"
          },
          "parcel_job": {
              "is_pickup_required": true,
              "pickup_service_type": "Scheduled",
              "pickup_service_level": "Premium",
              "pickup_address_id": "123456",       
              "pickup_date": "2021-12-15",
              "pickup_timeslot": {
                  "start_time": "09:00",
                  "end_time": "10:30",
                  "timezone": "Asia/Singapore"
              },
              "pickup_instructions": "Parcels will be ready by 9 AM, and I'm leaving at 10:31 AM.",
              "delivery_start_date": "2021-12-16",
              "delivery_timeslot": {
                  "start_time": "09:00",
                  "end_time": "22:00",
                  "timezone": "Asia/Singapore"
              },
              "delivery_instructions": "Please be careful with as the packages are heavy",
              "dimensions": {
                  "weight": 1.0,
                  "size": "L"
              }
          }
        }
      
      ```

      # Zone code mapping
      This section describes the zone codes for the [Tariff API](#tag/Tariff-API). 
      Zone codes are used as identifiers to distinguish locations at granular levels, which vary by country.

      ## Indonesia 
      In Indonesia, zone codes are used to distinguish locations up to the Kecamatan (district) level.
      Each district is represented by 2 codes: `L1 tier code` for its Provinsi (province), and `L2 tier code` for its Kota/Kabupaten (city/regency) and Kecamatan.

      The mapping data is available as a CSV file: <a href="/static/media/ID_zone_code_mapping.ae6d8012.csv">ID_zone_code_mapping.csv</a>.

paths:
  '/{countryCode}/2.0/oauth/access_token':
    post:
      tags:
        - OAuth API
      summary: Request access token
      description: |-
        Returns a new OAuth Bearer access token. 
        
        ### Prerequisites

        To make requests to this endpoint, you need to have the Client ID and Client Secret credentials. For more information, see [Authentication](#section/Introduction/Authentication). 

        ### Implementation flow
        
        Your implementation must adhere to this flow:
        1. Generate an access token.
        2. Store the token, along with the expiration timestamp, in a persistent storage.
        3. Attach the token as a Bearer token with every API request to a Ninja Van API. This means that for the `Authorization` HTTP header, the value must be `Bearer <INSERT ACCESS TOKEN>`.
        4. Five minutes before the token expires, or if a request to a Ninja Van API returns an HTTP `401` status code, generate a new token.

        Validity period is dynamic and may change from time to time. The minimum validity period is *one hour*.
        
        **Note:** Always set `grant_type` to `client_credentials`.

      parameters:
        - name: countryCode
          in: path
          description: Country Code
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccessTokenGrantRequest'
        required: false
      responses:
        '200':
          description: Access token was successfully generated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccessTokenGrantResponse'
        '400':
          description: Access token wasn't generated because of an issue with the request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Access token wasn't generated because of an issue with the server.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request POST 
              'https://api-sandbox.ninjavan.co/sg/2.0/oauth/access_token' \
                --header 'Content-Type: application/json' \
                --data-raw '{
                  "client_id": "string",
                  "client_secret": "string",
                  "grant_type": "client_credentials"
                }'
      x-codegen-request-body-name: body
    x-summary: Generate OAuth Bearer access token
  '/{countryCode}/4.1/orders':
    post:
      tags:
        - Order API
      summary: Create order
      description: |-
        Creates a new delivery order.  

        To avoid creating duplicate orders, we recommend sending a `requested_tracking_number` so that the API can use it to generate the waybill tracking number.

        For marketplace platforms, `marketplace` information is required. Using the provided `seller_id`, the API automatically creates a new Ninja Van shipper account for your marketplace seller, if the `seller_id` wasn't previously registered in Ninja Van's systems.

        For corporate accounts, `corporate` information is required. The corporate branch account has to be created under the corporate HQ before an order creation request can be made. Using `branch_id` field, the API creates orders for the corporate branch identified by the given branch ID.
      parameters:
        - name: Authorization
          in: header
          description: Your Bearer token.
          required: true
          schema:
            type: string
        - name: countryCode
          in: path
          description: Country code.
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewOrderV4.1'
        required: false
      responses:
        '200':
          description: Order successfully created.
          content:
            application/json:
              schema:
                allOf:
                  - required:
                      - tracking_number
                    type: object
                    properties:
                      requested_tracking_number:
                        minLength: 9
                        pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
                        type: string
                        description: |-
                          The requested tracking number of the order.
                      tracking_number:
                        pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
                        type: string
                      service_type:
                        $ref: '#/components/schemas/ServiceType'
                      service_level:
                        $ref: '#/components/schemas/ServiceLevel'
                      reference:
                        type: object
                        properties:
                          merchant_order_number:
                            maxLength: 255
                            type: string
                            description: Identifier of the order in shipper's systems.
                        description: 'Reference information.'
                      from:
                        type: object
                        description: |-
                          Sender's information.
                        allOf:
                          - $ref: '#/components/schemas/NewSender'
                      to:
                        type: object
                        description: |-
                          Recipient's information.
                        allOf:
                          - $ref: '#/components/schemas/NewRecipient'
                      parcel_job:
                        type: object
                        description: |-
                          Details of the parcel's pickup and delivery jobs.
                        allOf:
                          - $ref: '#/components/schemas/ParcelJob'
                    example:
                      requested_tracking_number: 1234-56789
                      tracking_number: PREFIX1234-56789
                      service_type: Parcel
                      service_level: Standard
                      reference:
                        merchant_order_number: SHIP-1234-56789
                      from:
                        name: John Doe
                        phone_number: '+60138201527'
                        email: john.doe@gmail.com
                        address:
                          address1: 17 Lorong Jambu 3
                          address2: ''
                          area: Taman Sri Delima
                          city: Simpang Ampat
                          state: Pulau Pinang
                          address_type: office
                          country: MY
                          postcode: '51200'
                      to:
                        name: Jane Doe
                        phone_number: '+60103067174'
                        email: jane.doe@gmail.com
                        address:
                          address1: Jalan PJU 8/8
                          address2: ''
                          area: Damansara Perdana
                          city: Petaling Jaya
                          state: Selangor
                          address_type: home
                          country: MY
                          postcode: '47820'
                      parcel_job:
                        is_pickup_required: true
                        pickup_service_type: Scheduled
                        pickup_service_level: Standard
                        pickup_address_id: '98989012'
                        pickup_date: '2021-12-15'
                        pickup_timeslot:
                          start_time: '09:00'
                          end_time: '12:00'
                          timezone: Asia/Kuala_Lumpur
                        pickup_approximate_volume: Less than 3 Parcels
                        pickup_instructions: Pickup with care!
                        delivery_start_date: '2021-12-16'
                        delivery_timeslot:
                          start_time: '09:00'
                          end_time: '12:00'
                          timezone: Asia/Kuala_Lumpur
                        delivery_instructions: 'If recipient is not around, leave parcel in power riser.'
                        allow-weekend_delivery: true
                        dimensions:
                          weight: 1.5
                        items:
                          - item_description: Sample description
                            quantity: 1
                            is_dangerous_good: false
        '400':
          description: Order wasn't created because of an issue with the request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                service-level-not-supported:
                  summary: Service level not supported
                  value:
                    error:
                      request_id: 1ba6da4f-0709-416e-9e30-a5546130b4d2
                      title: Invalid Data
                      message: Please check your request payload for validation errors.
                      details:
                        - reason: Validation Error
                          message: Service Level is not supported for this shipper account
                utf-extended-not-supported:
                  summary: UTF extended charset not supported
                  value:
                    error:
                      code: 127014
                      title: Bad Request
                      message: Invalid charset! UTF Extended charset is not supported
                duplicate-tracking-id:
                  summary: Duplicate tracking ID
                  value:
                    error:
                      code: 109201
                      request_id: 1ba6da4f-0709-416e-9e30-a5546130b4d2
                      title: Duplicate Tracking ID
                      message: Tracking ID (123456789) has been used, please use a different tracking ID and try again
                invalid-timeslot:
                  summary: Invalid pickup or delivery time slot
                  value:
                    error:
                      request_id: 1ba6da4f-0709-416e-9e30-a5546130b4d2
                      title: Invalid Data
                      message: Please check your request payload for validation errors.
                      details:
                        - reason: Validation Error
                          message: Invalid timeslot provided. Please check the API docs for a list of valid time slots.
                invalid-contact-info:
                  summary: Contact information not provided
                  value:
                    error:
                      request_id: 1ba6da4f-0709-416e-9e30-a5546130b4d2
                      title: Invalid Data
                      message: Please check your request payload for validation errors.
                      details:
                        - reason: Validation Error
                          field: null
                          message: "'Invalid contact information. Please provide either a 'email' or 'phone_number'."
                dimension-info-not-provided:
                  summary: Dimension information not provided
                  value:
                    error:
                      request_id: 1ba6da4f-0709-416e-9e30-a5546130b4d2
                      title: Invalid Data
                      message: Please check your request payload for validation errors.
                      details:
                        - reason: Validation Error
                          message: At least a form of dimension must be provided. Please check with your shipper support team to see which one you should be providing.
                          field: parcel_job.dimensions
                cod-not-configured:
                  summary: Cash on delivery not configured
                  value:
                    error:
                      request_id: 1ba6da4f-0709-416e-9e30-a5546130b4d2
                      title: Invalid Data
                      message: Please check your request payload for validation errors.
                      details:
                        - reason: Validation Error
                          field: null
                          message: "Your account isn't configured for Cash on Delivery. Please check with your shipper support team on enabling this service."
                invalid-requested-tracking-number:
                  summary: Invalid requested tracking number
                  value:
                    error:
                      request_id: 1ba6da4f-0709-416e-9e30-a5546130b4d2
                      title: Invalid Data
                      message: Please check your request payload for validation errors.
                      details:
                        - reason: Validation Error
                          field: null
                          message: "The 'requested_tracking_number' field must be between 1 and 9 characters (please do not pass a prefix), and can contain alphanumeric characters, and the (-) dash symbol."
                invalid-requested-tracking-prefix:
                  summary: Invalid requested tracking prefix
                  value:
                    error:
                      request_id: 1ba6da4f-0709-416e-9e30-a5546130b4d2
                      title: Invalid Data
                      message: Please check your request payload for validation errors.
                      details:
                        - reason: Validation Error
                          field: null
                          message: "The 'requested_tracking_number' field must start with the correct prefix."
        '500':
          description: Order wasn't created because of an issue with the server.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request POST 'https://api-sandbox.ninjavan.co/sg/4.1/orders' \
            --header 'Authorization: Bearer string' \
            --header 'Content-Type: application/json' \
            --data-raw '{
              "service_type": "Parcel",
              "service_level": "Standard",
              "requested_tracking_number": "1234-56789",
              "reference": {
                "merchant_order_number": "SHIP-1234-56789"
                },
              "from": {
                "name": "John Doe",
                "phone_number": "+60138201527",
                "email": "john.doe@gmail.com",
                "address": {
                  "address1": "17 Lorong Jambu 3",
                  "address2": "",
                  "area": "Taman Sri Delima",
                  "city": "Simpang Ampat",
                  "state": "Pulau Pinang",
                  "address_type": "office",
                  "country": "MY",
                  "postcode": "51200"
                  }
                },
                "to": {
                "name": "Jane Doe",
                "phone_number": "+60103067174",
                "email": "jane.doe@gmail.com",
                "address": {
                  "address1": "Jalan PJU 8/8",
                  "address2": "",
                  "area": "Damansara Perdana",
                  "city": "Petaling Jaya",
                  "state": "Selangor",
                  "address_type": "home",
                  "country": "MY",
                  "postcode": "47820"
                  }
                },
            "parcel_job": {
              "is_pickup_required": true,
              "pickup_service_type": "Scheduled",
              "pickup_service_level": "Standard",
              "pickup_address_id": "98989012",
              "pickup_date": "2021-12-15",
              "pickup_timeslot": {
                "start_time": "09:00",
                "end_time": "12:00",
                "timezone": "Asia/Kuala_Lumpur"
                },
              "pickup_instructions": "Pickup with care!",
              "delivery_instructions": "If recipient is not around, leave parcel in power riser.",
              "delivery_start_date": "2021-12-16",
              "delivery_timeslot": {
                "start_time": "09:00",
                "end_time": "22:00",
                "timezone": "Asia/Kuala_Lumpur"
                },
              "dimensions": {
                  "weight": 1.5
                },
              "items": [
                {
                  "item_description": "Sample description",
                  "quantity": 1,
                  "is_dangerous_good": false
                }
              ]
              }
            }'
      x-codegen-request-body-name: body
    x-summary: Create Order (v4.1 - Latest)
  '/{countryCode}/2.0/reports/waybill':
    get:
      tags:
        - Order API
      summary: Generate waybill
      description: |-
        Generates a waybill for an order.

        You can generate a waybill only for an order that is successfully processed. After an order creation request is accepted by the order creation endpoint, the order goes into a queue for further processing. 
        When it's fully processed, the platform generates a Pending Pickup webhook (see [Sample webhook payloads](#section/Receive-a-webhook/Sample-webhook-payloads)). This webhook gives your system an indication that a waybill can be generated.
        
        ### How to request access
        By default, Ninja Van doesn't grant the permission to use the waybill generation endpoint to any shipper, even in the sandbox environment. To request access, take these steps:
        
        1. Review the [Integration requirements](#section/Introduction/Integration-requirements).
        2. Send an email to [developer support](#section/Introduction/Contact-information).

      parameters:
        - name: Authorization
          in: header
          description: Your Bearer token.
          required: true
          schema:
            type: string
        - name: countryCode
          in: path
          description: Country code.
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
        - name: tids
          in: query
          description: The `tracking_number` as generated by the Order API.
          required: true
          schema:
            minLength: 9
            pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
            type: string
        - name: h
          in: query
          description: A flag for hiding shipper's details, such as contact information, on the waybill. If no flag is provided with the query, the details are hidden. To explicitly hide the details, set to `1`. To show the details, set to `0`.
          schema:
            maximum: 1
            minimum: 0
            type: integer
            format: int32
        - name: orientation
          in: query
          description: Specifies the orientation of the generated waybill. The default value is `portrait`.
          schema:
            type: string
            enum:
              - portrait
              - landscape
      responses:
        '200':
          description: Waybill successfully generated. A PDF file will be returned in the response.
          content: {}
        '400':
          description: Waybill wasn't generated because of an invalid request.
          content: {}
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request GET \
              'https://api-sandbox.ninjavan.co/sg/2.0/reports/waybill?tids=:trackingNo&h=1' \
                --header 'Authorization: Bearer string'
      x-codegen-request-body-name: body
    x-summary: Generate Waybill
  '/{countryCode}/2.2/orders/{trackingNo}':
    delete:
      tags:
        - Order API
      summary: Cancel order
      description: |-
        Cancels an existing delivery order. Only orders that are Pending Pickup can be cancelled.

        **Note:** The names of the parameters in this endpoint differ from the [order creation](#tag/Order-API/paths/~1{countryCode}~14.1~1orders/post) endpoint. 
        This is because the two endpoints use different specification versions.

      parameters:
        - name: Authorization
          in: header
          description: Your Bearer token.
          required: true
          schema:
            type: string
        - name: countryCode
          in: path
          description: Country code.
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
        - name: trackingNo
          in: path
          description: The `tracking_number` that our API returned in the order creation response.
          required: true
          schema:
            minLength: 9
            pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
            type: string
      responses:
        '200':
          description: Order successfully cancelled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CancelOrderResponse'
        '400':
          description: Order wasn't cancelled because of an issue with the request.
          content: {}
        '404':
          description: Order wasn't cancelled because no orders were found that matched the query parameters.
          content: {}
        '500':
          description: Order wasn't cancelled because of an issue with the server.
          content: {}
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request DELETE \
              'https://api-sandbox.ninjavan.co/sg/2.2/orders/:trackingNo' \
                --header 'Authorization: Bearer string'
      x-codegen-request-body-name: body
    x-summary: Cancel Order
  '/{countryCode}/2.0/pudos':
    x-summary: List Ninja Points v2
    get:
      tags:
        - PUDO API
      summary: List Ninja Points v2
      description: >
        Returns a list of Ninja Points that meet the passed query parameters.
      parameters:
        - name: countryCode
          in: path
          description: Country code.
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
        - name: can_customer_collect
          in: query
          description: Set this to `true` to fetch the list of Ninja Points where the customer can collect parcels.
          required: false
          schema:
            type: boolean
        - name: allow_create_post
          in: query
          description: Set this to `true` to fetch the list of Ninja Points where the shipper can create "post" parcels.
          required: false
          schema:
            type: boolean
        - name: allow_customer_return
          in: query
          description: Set this to `true` to fetch the list of Ninja Points where the customer can return parcels.
          required: false
          schema:
            type: boolean
        - name: allow_shipper_send
          in: query
          description: Set this to `true` to fetch the list of Ninja Points where the shipper can drop off parcels.
          required: false
          schema:
            type: boolean
        - name: allow_create_pack
          in: query
          description: Set this to `true` to fetch the list of Ninja Points where the shipper can drop off "pack" parcels.
          required: false
          schema:
            type: boolean
        - name: can_sell_packs
          in: query
          description: Set this to `true` to fetch the list of Ninja Points where the shipper can buy "pack" parcels.
          required: false
          schema:
            type: boolean    
      responses:
        '200':
          description: Ninja points successfully listed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DistributionPointV2'
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request GET \
            'https://api-sandbox.ninjavan.co/sg/2.0/pudos?can_customer_collect=false&allow_create_post=false&allow_customer_return=false&allow_shipper_send=false&allow_create_pack=false&can_sell_packs=false'
      x-codegen-request-body-name: body
  '/{countryCode}/1.0/send-orders/{trackingId}':
    x-summary: Scan parcel for shipper drop-off
    get:
      tags:
        - PUDO partners API
      summary: Scan parcel for shipper drop-off
      description: |+
        Triggers parcel scanning for shipper drop-off at a Ninja Point. Returns order information.
      parameters:
        - name: countryCode
          in: path
          description: Country code.
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
        - name: trackingId
          in: path
          description: Tracking ID of the order.
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Order successfully scanned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendParcelScanResponse'
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request GET \
            'https://api-sandbox.ninjavan.co/sg/dp/1.0/external/orders/{trackingId}'
      x-codegen-request-body-name: body
  '/{countryCode}/1.0/send-orders/drop-off':
    x-summary: Trigger shipper drop-off for parcel
    post:
      tags:
        - PUDO partners API
      summary: Trigger shipper drop-off for parcel
      description: >
        Notifies Ninja Van that a parcel is being dropped off at a Ninja Point by a shipper. Returns parcels that are successfully dropped off and a list of errors if any.
      parameters:
        - name: countryCode
          in: path
          description: Country code.
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendWebhookRequest'
        required: true
      responses:
        '200':
          description: Parcels successfully dropped off (possibly some of them had errors).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendWebhookResponse'
        '400':
          description: Validation error (store doesn't exist or the list of tracking IDs is empty).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request POST \
            'https://api-sandbox.ninjavan.co/sg/dp/1.0/external/orders'
      x-codegen-request-body-name: body
  '/{countryCode}/1.0/dp-order-events':
    x-summary: Trigger webhooks for customer collection, driver drop-off, driver collection
    put:
      tags:
        - PUDO partners API
      summary: Trigger webhooks for customer collection, driver drop-off, driver collection
      description: |+
        Sends Ninja Van a webhook whenever one of the following events occur: 
        * Customer collects a parcel from a Ninja Point.
        * Driver drops off a parcel at a Ninja Point. 
        * Driver collects a parcel from a Ninja Point.
      parameters:
        - name: countryCode
          in: path
          description: Country code.
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DpOrderEventRequest'
            examples:
              store_received_from_driver:
                summary: Webshook for when the store received the parcel from the driver.
                value:
                  tracking_id: NVSGBEDBP03784ADPL
                  store_id: PSVKV4M16A
                  received_at: 2022-06-01 00:00:00
              store_released_to_customer:
                summary: Webhook for when the store released the parcel to the customer.
                value:
                  tracking_id: NVSGBEDBP03784ADPL
                  store_id: PSVKV4M16A
                  released_at: 2022-06-01 00:00:00
                  customer_unlock_code: 389123
              store_released_to_driver:
                summary: Webhook for when the store released the parcel to the driver.
                value:
                  tracking_id: NVSGBEDBP03784ADPL
                  store_id: PSVKV4M16A
                  released_at: 2022-06-01 00:00:00
        required: true
      responses:
        '202':
          description: Event successfully processed.
        '400':
          description: Validation error (the store doesn't exist or the list of tracking IDs is empty).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request PUT \
            'https://api-sandbox.ninjavan.co/sg/1.0/dp-order-events'
      x-codegen-request-body-name: body
  '/{countryCode}/1.0/public/price':
    x-summary: Get standard price
    post:
      tags:
        - Tariff API
      summary: Get price estimate
      description: |-
        Returns an estimated standard delivery fee for an order based on the provided `weight`, `service_level`, `from` and `to` locations, excluding any surcharge and discount. 

        **Note:** This endpoint doesn't require authentication but is rate-limited to 20 calls per minute.
      parameters:
        - name: countryCode
          in: path
          description: Country code. Only `ID` is supported.
          required: true
          schema:
            type: string
            enum:
              - ID
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PriceEstimate'
              examples:
                sample-billing-zone:
                  value:
                    weight: 2.38
                    service_level: Standard
                    from:
                      l1_tier_code: ID_A00007_01
                      l2_tier_code: ID_B00069_01
                    to:
                      l1_tier_code: ID_A00022_01
                      l2_tier_code: ID_B00296_01
                sample-billing-zone-name:
                  value:
                    weight: 2.38
                    service_level: Standard
                    from:
                      l1_name: Jabodetabek
                      l2_name: Kab. Kepulauan Seribu
                      l3_name: Kepulauan Seribu Selatan
                    to:
                      l1_name: Kupang
                      l2_name: Kab. Kupang
                      l3_name: Amabi Oefeto
        required: true
      responses:
        '200':
          description: Obtained a price estimate successfully.
          content:
            application/json:
              schema:
                  type: object
                  required:
                    - data
                  properties:
                    data:
                      type: object
                      properties:
                        total_fee:
                          type: number
                          description: The estimated price. May be expressed in exponential notation.
                          example: 9E+4
        '404':
          description: Origin-Destination (OD) pair error. Cannot find a price estimate because the given from and to zone pair does not exist in the rate card. This may mean that the submitted zone codes are invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: "1002"
                  request_id: "7a67b378-bf21-4de6-b982-3cb10ee5b1f6"
                  title: "No OD Pair in Script"
                  message: "Pricing Script and Billing Zones are present, but there's no existing OD pair"
  '/{countryCode}/1.0/orders/tracking-events/{trackingNumber}':
    x-summary: Get events for single parcel
    get:
      tags:
        -  Tracking API
      summary: Get events for single parcel
      description: >
        Get a list of tracking events of a single parcel by tracking number.

        ### How to request access
          Access to this endpoint is conditional. By default, Ninja Van doesn't grant the permission to use this endpoint to any shipper, even in the sandbox environment. To request access, take these steps:
        
          1. Review the [Integration requirements](#section/Introduction/Integration-requirements).
          2. Send an email to [developer support](#section/Introduction/Contact-information).
      parameters:
        - name: Authorization
          in: header
          description: Your Bearer token.
          required: true
          schema:
            type: string
        - name: countryCode
          in: path
          description: Country code.
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
        - name: trackingNumber
          in: path
          description: The `tracking_number` as generated by the Order API.
          required: true
          schema:
            minLength: 9
            pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
            type: string
      responses:
        '200':
          description: Events retrieved successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SingleTrackingNumberResponse'
        '400':
          description: Events weren't retrieved because of an issue with the request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request GET \
              'https://api-sandbox.ninjavan.co/sg/1.0/orders/tracking-events/:trackingNumber' \
                --header 'Authorization: Bearer string'
      x-codegen-request-body-name: body
  '/{countryCode}/1.0/orders/tracking-events':
    x-summary: Get events for list of parcels
    get:
      tags:
        -  Tracking API
      summary: Get events for list of parcels
      description: >
        Retrieve tracking events for a list of tracking numbers.

        ### How to request access
          Access to this endpoint is conditional. By default, Ninja Van doesn't grant the permission to use this endpoint to any shipper, even in the sandbox environment. To request access, take these steps:
          
          1. Review the [Integration requirements](#section/Introduction/Integration-requirements).
          2. Send an email to [developer support](#section/Introduction/Contact-information).
      parameters:
        - name: Authorization
          in: header
          description: Your Bearer token.
          required: true
          schema:
            type: string
        - name: countryCode
          in: path
          description: Country code.
          required: true
          schema:
            type: string
            enum:
              - SG
              - MY
              - TH
              - ID
              - VN
              - PH
              - MM
        - name: tracking_number
          in: query
          description: The list of tracking_number to track, limited to 50 per request.
          required: true
          schema:
            minLength: 9
            pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
            type: string
      responses:
        '200':
          description: Events retrieved successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MultipleTrackingNumbersResponse'
        '400':
          description: Events weren't retrieved because of an issue with the request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-code-samples:
        - lang: curl
          source: |-
            curl --location --request GET \
              'https://api-sandbox.ninjavan.co/sg/1.0/orders/tracking-events?tracking_number=:trackingNumber1&tracking_number=:trackingNumber2' \
                --header 'Authorization: Bearer string'
      x-codegen-request-body-name: body


components:
  schemas:
    LegacyOrder:
      type: object
      properties:
        tracking_number:
          type: string
        requested_tracking_number:
          type: string
        order_ref_no:
          type: string
        type:
          type: string
        from_postcode:
          type: string
        from_address1:
          type: string
        from_address2:
          type: string
        from_locality:
          type: string
        from_city:
          type: string
        from_country:
          type: string
        from_email:
          type: string
        from_name:
          type: string
        from_contact:
          type: string
        to_postcode:
          type: string
        to_address1:
          type: string
        to_address2:
          type: string
        to_locality:
          type: string
        to_city:
          type: string
        to_country:
          type: string
        to_email:
          type: string
        to_name:
          type: string
        to_contact:
          type: string
        pickup_instructions:
          type: string
        delivery_instructions:
          type: string
        delivery_date:
          type: string
        parcel_size:
          type: integer
        parcel_volume:
          type: integer
          format: double
        parcel_weight:
          type: integer
          format: double
        max_delivery_days:
          maximum: 10
          minimum: 0
          type: integer
          format: int32
        id:
          type: string
          description: Async handle of the order.
    Parcel:
      type: object
      description: Parcel service representation
      allOf:
        - $ref: '#/components/schemas/NewOrderV4.1'
    Marketplace:
      type: object
      description: marketplace order representation
      properties:
        marketplace:
          $ref: '#/components/schemas/MarketplaceOrder'
      allOf:
        - $ref: '#/components/schemas/NewOrderV4.1'
    Corporate:
      type: object
      description: corporate order representation
      properties:
        corporate:
          $ref: '#/components/schemas/CorporateOrder'
      allOf:
        - $ref: '#/components/schemas/NewOrderV4.1'
    International:
      type: object
      description: international order representation
      properties:
        international:
          $ref: '#/components/schemas/InternationalOrder'
        experimental_customs_declaration:
          $ref: '#/components/schemas/CustomsDeclaration'
      allOf:
        - $ref: '#/components/schemas/NewOrderV4.1'
      example:
        international:
          portation: import
        experimental_customs_declaration:
          customs_description: men's shirts
          native_customs_description: 男人衬衫
          goods_currency: USD
          goods_value: '15.99'
          battery_type: No Battery
          battery_packing: No Battery
          hs_code: '543111'
          trade_terms: DDU
          invoice_url: www.invoice.com/12345.pdf
        service_type: International
        service_level: Standard
        requested_tracking_number: 1234-56789
        reference:
          merchant_order_number: SHIP-1234-56789
        from:
          name: John Doe
          phone_number: '+60138201527'
          email: john.doe@gmail.com
          address:
            address1: 17 Lorong Jambu 3
            address2: ''
            area: Taman Sri Delima
            city: Simpang Ampat
            state: Pulau Pinang
            address_type: office
            country: MY
            postcode: '51200'
        to:
          name: Jane Doe
          phone_number: '+6502700553'
          email: jane.doe@gmail.com
          address:
            address1: 69 Lorong 6 Geylang
            address2: ''
            country: SG
            postcode: '399221'
        parcel_job:
          is_pickup_required: false
          delivery_instructions: 'If recipient is not around, leave parcel in power riser.'
          delivery_start_date: '2021-12-16'
          delivery_timeslot:
            start_time: '09:00'
            end_time: '12:00'
            timezone: Asia/Singapore
          dimensions:
            weight: 1.5
    NewOrderV4.1:
      required:
        - from
        - parcel_job
        - service_level
        - service_type
        - to
      type: object
      discriminator:
        propertyName: service_type
        mapping:
          Parcel: '#/components/schemas/Parcel'
          Marketplace: '#/components/schemas/Marketplace'
          Corporate: '#/components/schemas/Corporate'
          International: '#/components/schemas/International'
          Document: '#/components/schemas/Parcel'
          Return: '#/components/schemas/Parcel'
          Bulky: '#/components/schemas/Parcel'
      properties:
        service_type:
          $ref: '#/components/schemas/ServiceType'
        service_level:
          $ref: '#/components/schemas/ServiceLevel'
        requested_tracking_number:
          minLength: 9
          pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
          type: string
          description: |-
            If your shipper account is a prefixless account, then `requested_tracking_number` is _required_.

            If your shipper account has a tracking number prefix configured, then the API concatenates your prefix with the `requested_tracking_number` to generate the waybill tracking number.

            If `requested_tracking_number` isn't provided, then the API automatically generates a random tracking number and concatenates your prefix with it to generate the waybill tracking number. 
                          
            A generic prefix is used for this request, if your account does not have a prefix _and_ if the `requested_tracking_number` is not provided.

            The total length of the generated `tracking_number` is further validated based on your shipper settings. 
                          
            The default length for `tracking_number` is around 18 characters. If the `requested_tracking_number` is too short, the API auto-pads the value with zeros (`0`). 
                          
            Check with us if you have any questions regarding your shipper account settings.
        reference:
          type: object
          properties:
            merchant_order_number:
              maxLength: 255
              type: string
              description: Identifier of the order in shipper's systems.
          description: 'Reference information. This information returns to the shipper via webhooks, and can be used as query parameters when retrieving orders from our API.'
        from:
          type: object
          description: |-
            Sender's information. Used for the following:
            * Contact details for informing the sender regarding parcel updates.
            * Address details to be printed on the waybill.
          allOf:
            - $ref: '#/components/schemas/NewSender'
        to:
          type: object
          description: |-
            Recipient's information. Used for the following:
            * Delivery destination.
            * Contact details for informing the recipient regarding parcel updates.
            * Address details to be printed on the waybill.
          allOf:
            - $ref: '#/components/schemas/NewRecipient'
        parcel_job:
          type: object
          description: |-
            Details of the parcel's pickup and delivery jobs.

            If no pickup address is provided, Ninja Van uses the default pickup address specified in the shipper pickup reservation settings.
          allOf:
            - $ref: '#/components/schemas/ParcelJob'
      example:
        service_type: Parcel
        service_level: Standard
        requested_tracking_number: 1234-56789
        reference:
          merchant_order_number: SHIP-1234-56789
        from:
          name: John Doe
          phone_number: '+60138201527'
          email: john.doe@gmail.com
          address:
            address1: 17 Lorong Jambu 3
            address2: ''
            area: Taman Sri Delima
            city: Simpang Ampat
            state: Pulau Pinang
            address_type: office
            country: MY
            postcode: '51200'
        to:
          name: Jane Doe
          phone_number: '+60103067174'
          email: jane.doe@gmail.com
          address:
            address1: Jalan PJU 8/8
            address2: ''
            area: Damansara Perdana
            city: Petaling Jaya
            state: Selangor
            address_type: home
            country: MY
            postcode: '47820'
        parcel_job:
          is_pickup_required: true
          pickup_address_id: '98989012'
          pickup_service_type: Scheduled
          pickup_service_level: Standard
          pickup_date: '2021-12-15'
          pickup_timeslot:
            start_time: '09:00'
            end_time: '12:00'
            timezone: Asia/Kuala_Lumpur
          pickup_instructions: Pickup with care!
          delivery_instructions: 'If recipient is not around, leave parcel in power riser.'
          delivery_start_date: '2021-12-16'
          delivery_timeslot:
            start_time: '09:00'
            end_time: '12:00'
            timezone: Asia/Kuala_Lumpur
          dimensions:
            weight: 1.5
          items:
            - item_description: Sample description
              quantity: 1
              is_dangerous_good: false
    MarketplaceOrder:
      required:
        - seller_company_name
        - seller_id
      type: object
      properties:
        seller_id:
          maxLength: 255
          minLength: 1
          pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
          type: string
          description: 'External reference for the marketplace seller. If your platform is a marketplace, and you are creating an order for a seller on your platform, pass the unique seller ID in this field.'
          example: John-Doe-Shop
        seller_company_name:
          maxLength: 255
          minLength: 2
          type: string
          description: Company name of this marketplace seller.
          example: John Doe Shop
      description: Required if the order type is `Marketplace`.
    CorporateOrder:
      required:
        - branch_id
      type: object
      properties:
        branch_id:
          maxLength: 255
          minLength: 1
          pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
          type: string
          description: 'External reference for the corporate branch.'
          example: Branch-1234
      description: Required if the order type is `Corporate`.
    InternationalOrder:
      required:
        - portation
      type: object
      properties:
        portation:
          type: string
          description: |-
            For orders with goods that have been or need to be imported into the country, whether by a forwarder or by Ninja Van, set `Import` as the portation type.

            For orders that require goods to be exported by Ninja Van to another country, set `Export` as the portation type.
          enum:
            - Import
            - Export
      description: Details specific to cross-border.
      example:
    CustomsDeclaration:
      type: object
      properties:
        customs_description:
          type: string
          maxLength: 255
          description: |-
            Details of goods imported or exported.
          example: men’s shirts
        native_customs_description:
          type: string
          description: |-
            Details of goods in native language.
          example:
            男人衬衫
        goods_currency:
          type: string
          description: |-
            Currency of goods value.
          example: USD
        goods_value:
          type: string
          description: |-
            Goods value for customs declaration.
          example: '15.99'
        battery_type:
          type: string
          description: |-
            Battery type.
          example: No Battery
        battery_packing:
          type: string
          description: |-
            Battery package.
          example: No Battery
        hs_code:
          type: string
          description: |-
            Harmonized System (HS) code to determine the type of goods and their associated tax rate.
          example: '543111'
        trade_terms:
          type: string
          description: |-
            Trade terms.
          example: DDU
        invoice_url:
          type: string
          description: |-
            Invoice URL.
          example: www.invoice.com/12345.pdf
      description: Details specific to cross-border. Applies only to `International` orders.
    NewSender:
      required:
        - address
        - email
        - name
        - phone_number
      type: object
      properties:
        name:
          maxLength: 255
          minLength: 3
          type: string
          example: John Doe
        phone_number:
          maxLength: 32
          minLength: 6
          type: string
          description: |-
            Phone number of the contact.
    
            The API attempts to format the number based on the E.164 format, using the country code of the address provided. If it can be formatted, then the number is considered valid.
    
            The API only requires that either the `email` or `phone_number` is valid.
          example: '+6563337193'
        email:
          type: string
          description: |-
            Email address of the contact.
    
            Must be a valid email address format. The formal definitions are in RFC 5322 (sections 3.2.3 and 3.4.1) and RFC 5321, with a more readable form given in the informational RFC 3696 and the associated errata.
    
            The API only requires that either the `email` or `phone_number` is valid. If an email is provided, the API validates it and returns an error if the validation fails.
          format: email
          example: john.doe@mail.com
        address:
          $ref: '#/components/schemas/NewAddress'
        collection_point:
          type: string
          description: 'This field is experimental.'
          enum:
            - Ninja PUDO @ Orchard
            - Ninja PUDO @ NUH
    NewRecipient:
      required:
        - address
        - email
        - name
        - phone_number
      type: object
      properties:
        name:
          maxLength: 255
          minLength: 3
          type: string
          example: Jane Doe
        phone_number:
          maxLength: 32
          minLength: 6
          type: string
          description: |-
            Phone number of the contact.

            The API attempts to format the number based on the E.164 format, using the country code of the address provided. If it can be formatted, then the number is considered valid.

            The API only requires that either the `email` or `phone_number` is valid.
          example: '+6502700553'
        email:
          type: string
          description: |-
            Email address of the contact.

            Must be a valid email address format. The formal definitions are in RFC 5322 (sections 3.2.3 and 3.4.1) and RFC 5321, with a more readable form given in the informational RFC 3696 and the associated errata.

            The API only requires that either the `email` or `phone_number` is valid. If an email is provided, the API validates it and returns an error if the validation fails.
          format: email
          example: jane.doe@mail.com
        address:
          $ref: '#/components/schemas/NewAddress'
        collection_point:
          type: string
          description: 'This field is experimental.'
          enum:
            - Ninja PUDO @ Orchard
            - Ninja PUDO @ NUH
    ParcelJob:
      required:
        - delivery_start_date
        - delivery_timeslot
        - is_pickup_required
      type: object
      properties:
        delivery_start_date:
          pattern: '^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))$'
          type: string
          description: |-
            Specifies the start date of the delivery period in the `yyyy-MM-dd` format. For example, for standard orders that take 3 days, if the `delivery_start_date` is on `2021-12-15`, the delivery will be attempted at any time from `2021-12-15` to `2021-12-17`.

            The API automatically adjust the date for blocked dates. Double check for the adjusted `delivery_start_date` in response.

            If an order is created before the cutoff time, the API defaults `delivery_start_date` to the same day, if that day is not a blocked date. Otherwise, `delivery_start_date` is defaulted to the next available date.
        delivery_timeslot:
          $ref: '#/components/schemas/DeliveryTimeslotInfo'
        delivery_instructions:
          type: string
          description: Delivery instructions for the driver.
        items:
          type: array
          description: |-
            Number of items in the list cannot exceed 200.
          items:
            $ref: '#/components/schemas/Items'
        allow_self_collection:
          type: boolean
          description: |-
            Indicates whether the customer is allowed to self-collect the parcels.
            *Required* if a valid delivery collection point (`to.collection_point`) is specified.
        allow_weekend_delivery:
          type: boolean
          description: Specifies whether deliveries can be attempted on weekends.
          default: true
        cash_on_delivery:
          type: number
          description: Specifies the amount of cash to be collected from the recipient upon delivery.
          format: double
        insured_value:
          type: number
          description: Specifies the desired insured value of the parcel.
          format: double
        dimensions:
          $ref: '#/components/schemas/Dimension'
        is_pickup_required:
          type: boolean
          description: |-
            Indicates whether a pickup reservation is required.
            If set to `true`, the system checks if a pickup reservation exists with the same:
            * `pickup_date`
            * Pickup address (inferred from the `parcel_job.pickup_address` or `from.address`)

            The system creates a new pickup reservation if none exists for the current pickup date and address.

            To update the pickup address for a specific `pickup_address_slot_id`, use one of these options:
            * Call Ninja Van's shipper support hotline.
            * Update the address tied to the `pickup_address_id` via the pickup reservation API.
            * Send a new order with an updated pickup address.
                * The pickup address specified in either the `from` or `pickup_address` field will be used to update the address
                tied to the `pickup_address_id`.
                
                * Existing pickup reservations tied to this `pickup_address_id`, that are already in progress, won't be modified in any way.
                
                * You account can be configured to _not_ update future pickup reservation addresses even if the address tied to the `pickup_address_id` has changed.

            To update the pickup time slot for a specific pickup reservation, use one of these options:
            * Call Ninja Van's shipper support hotline.
            * Send a new order with the same `pickup_address_id` and `pickup_date`, but with a different `pickup_timeslot`.

            For more details, refer to the notes for the `pickup_timeslot` and `pickup_address_id` fields.

            If you require a `Scheduled` pickup, then provide the following fields:
            * `pickup_date`
            * `pickup_timeslot`
            * `pickup_approximate_volume`
          default: false
      discriminator:
        propertyName: is_pickup_required
        mapping:
          'false': '#/components/schemas/NoPickupInfo'
          'true': '#/components/schemas/PickupInfo'
    NoPickupInfo:
      type: object
      allOf:
        - $ref: '#/components/schemas/ParcelJob'
    PickupInfo:
      allOf:
        - $ref: '#/components/schemas/ParcelJob'
        - type: object
          properties:
            pickup_service_type:
              type: string
              description: |-
                Specifies the pickup service type.
              default: Scheduled
            pickup_service_level:
              type: string
              description: |-
                Specifies the pickup service level. You have the option of choosing `Standard` or `Premium` levels.

                Premium pickups have shorter pickup time slots, and the cutoff rules are more relaxed. Check with your Account Manager to see if your account is eligible for Premium pickups.
              default: Standard
              enum:
                - Standard
                - Premium
            pickup_address:
              $ref: '#/components/schemas/NewSender'
            pickup_address_id:
              maxLength: 64
              minLength: 1
              pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
              type: string
              description: |-
                For each order creation request, if a pickup is requested, the system checks if the specified pickup address (inferred from the `from` or `pickup_address` field) is different from the existing address stored with the specified `pickup_address_id`.

                This `pickup_address_id` can be mapped to the internal ID of the address in your database. If you store the address in a database table, pass the address ID from the database as a way to provide Ninja Van with a `pickup_address_id`.

                If the specified `pickup_address_id` is not mapped to an existing address, the system does this:
                * Performs a `POST` operation and creates a new address, mapped to the provided `pickup_address_id`.

                If the specified pickup address is different from the existing address stored with the specified `pickup_address_id`, the system does this:
                * Performs a `PUT` operation and replaces the address stored with the specified `pickup_address_id`.
                * Updates the address for all future pickup reservations tied to the old address.
            pickup_date:
              pattern: '^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))$'
              type: string
              description: |-
                Specifies the date on which the pickup will occur in the `yyyy-MM-dd` format.

                The system will adjust the pickup date automatically to the next available working day, if the specified pickup date:
                * Falls on a blocked date. For example, a public holiday or warehouse closure.
                * Doesn't meet the cutoff time for pickup bookings for that day.

                Any adjustments to the pickup date are returned in the response.
            pickup_timeslot:
              $ref: '#/components/schemas/PickupTimeslotInfo'
            pickup_approximate_volume:
              type: string
              default: Less than 3 Parcels
              enum:
                - Full-Van Load
                - Half-Van Load
                - Larger than Van Load
                - Less than 10 Parcels
                - Less than 3 Parcels
                - Trolley Required
            pickup_instructions:
              type: string
              description: Instruction to driver for pickup attempts.
    NewAddress:
      required:
        - address1
      oneOf:
        - $ref: '#/components/schemas/IndonesiaAddress'
        - $ref: '#/components/schemas/MyanmarAddress'
        - $ref: '#/components/schemas/MalaysiaAddress'
        - $ref: '#/components/schemas/PhilippinesAddress'
        - $ref: '#/components/schemas/SingaporeAddress'
        - $ref: '#/components/schemas/ThailandAddress'
        - $ref: '#/components/schemas/VietnamAddress'
      properties:
        country:
          $ref: '#/components/schemas/CountryCode'
        address1:
          type: string
          example: 30 Ninja Street
          description: 'If you are collecting granular address information, concatenate the building number, building name, and street information in this field.'
        address2:
          type: string
          example: '#01-01'
        address_type:
          $ref: '#/components/schemas/AddressType'
        latitude:
          type: number
          format: double
          minimum: -90
          maximum: 90
        longitude:
          type: number
          format: double
          minimum: -180
          maximum: 180
    IndonesiaAddress:
      type: object
      properties:
        kelurahan:
          type: string
          description: For ID addresses.
        kecamatan:
          type: string
          description: For ID addresses.
        city:
          type: string
          description: For ID addresses.
        province:
          type: string
          description: For ID addresses.
        postcode:
          type: string
          description: Postal code for ID addresses.
      description: Indonesia-specific address fields.
      example:
        kelurahan: Kelurahan Gambir
        kecamatan: Kecamatan Gambir
        city: Jakarta Selatan
        province: Jakarta
        postcode: '10110'
    MyanmarAddress:
      required:
        - postcode
      type: object
      properties:
        township:
          type: string
          description: For MM addresses.
        district:
          type: string
          description: For MM addresses.
        state:
          type: string
          description: For MM addresses.
        postcode:
          type: string
          description: Postal code for MM addresses.
      description: Myanmar-specific address fields.
      example:
        township: San Lorenzo South Subdivision
        district: Malitlit
        state: LAGUNA
        postcode: '4026'
    MalaysiaAddress:
      required:
        - postcode
      type: object
      properties:
        area:
          type: string
          description: For MY addresses.
        city:
          type: string
          description: For MY addresses.
        state:
          type: string
          description: For MY addresses.
        postcode:
          type: string
          description: Postal-code for MY addresses.
      description: Malaysia-specific address fields.
      example:
        area: Taman Sri Delima
        city: Simpang Ampat
        state: Pulau Pinang
        postcode: '51200'
    PhilippinesAddress:
      type: object
      properties:
        subdivision:
          type: string
          description: For PH addresses.
        district:
          type: string
          description: For PH addresses.
        city:
          type: string
          description: For PH addresses.
        province:
          type: string
          description: For PH addresses.
        postcode:
          type: string
          description: Postal code for PH addresses.
      description: Philippines-specific address fields.
      example:
        subdivision: San Lorenzo South Subdivision
        district: Malitlit
        city: Santa Rosa
        province: LAGUNA
        postcode: '4026'
    SingaporeAddress:
      required:
        - postcode
      type: object
      properties:
        postcode:
          type: string
          description: Postal code for SG addresses.
      description: Singapore-specific address fields.
    ThailandAddress:
      required:
        - postcode
      type: object
      properties:
        sub_district:
          type: string
          description: For TH addresses.
        district:
          type: string
          description: For TH addresses.
        province:
          type: string
          description: For TH addresses.
        postcode:
          type: string
          description: Postal code for TH addresses.
      description: Thailand-specific address fields.
      example:
        sub_district: Bangyai
        district: Bangyai
        province: Nonthaburi
        postcode: '11140'
    VietnamAddress:
      type: object
      properties:
        ward:
          type: string
          description: For VN addresses. Here, enter the name of a ward in an urban district (such as "Phường Nguyễn Du" or "Phường 4"), or a provincial district-level sub-division (such as "Huyện Long Thành").
        district:
          type: string
          description: For VN addresses. Here, enter the name of a provincial city (such as "Thành phố Biên Hòa"), or an urban district (such as "Quận Hai Bà Trưng" in Hanoi or "Quận 4" in Ho Chi Minh).  
        city:
          type: string
          description: For VN addresses. Here, enter the name of a municipality (such as "Thành phố Hà Nội" or "Thành phố Hồ Chí Minh") or a province (such as "Tỉnh Đồng Nai").
      description: Vietnam-specific address fields.
      example:
        ward: Phường Nguyễn Du
        district: Quận Hai Bà Trưng
        city: Thành phố Hà Nội
    CountryCode:
      type: string
      example: SG
      enum:
        - SG
        - MY
        - TH
        - ID
        - VN
        - PH
        - MM
    AddressType:
      type: string
      description: Indicates the type of address. The value must be either `home` or `office`.
      enum:
        - home
        - office
    ServiceType:
      type: string
      description: Indicates the type of service. Used to inform Ninja Van on what jobs to schedule for this order.
      enum:
        - Parcel
        - Marketplace
        - Corporate
        - International
        - Bulky
        - Document
        - Return
    ServiceLevel:
      type: string
      description: Shipment service level.
      enum:
        - Standard
        - Express
        - Sameday
        - Nextday
    DeliveryTimeslotInfo:
      required:
        - end_time
        - start_time
      type: object
      properties:
        start_time:
          pattern: '^([0-1][0-9]|2[0-3]):[0-5][0-9]$'
          type: string
        end_time:
          pattern: '^([0-1][0-9]|2[0-3]):[0-5][0-9]$'
          type: string
        timezone:
          $ref: '#/components/schemas/Timezone'
      description: |-
        Delivery time slot information. The valid time slots are:

          * "09:00" to "12:00"
          * "09:00" to "18:00"
          * "09:00" to "22:00"
          * "12:00" to "15:00"
          * "15:00" to "18:00"
          * "18:00" to "22:00"
    PickupTimeslotInfo:
      required:
        - end_time
        - start_time
      type: object
      properties:
        start_time:
          pattern: '^([0-1][0-9]|2[0-3]):[0-5][0-9]$'
          type: string
        end_time:
          pattern: '^([0-1][0-9]|2[0-3]):[0-5][0-9]$'
          type: string
        timezone:
          $ref: '#/components/schemas/Timezone'
      description: |-
        Pickup time slot information. The valid time slots are:

          * "09:00" to "12:00"
          * "09:00" to "18:00"
          * "09:00" to "22:00"
          * "12:00" to "15:00"
          * "15:00" to "18:00"
          * "18:00" to "22:00"

        When scheduling a pickup, keep in mind our cutoff rules for the Standard and Premium service levels.

        Standard service level:
        * If you created an order before the cutoff time, but the pickup end time is less than 3 hours from now, then the pickup will roll over to the next working day.
        * If you created an order before the cutoff time, and the pickup end time is 3 hours or more from now, then the pickup can be created on the same day.
        * If you created an order after the cutoff time, then the pickup will roll over to the next working day.

        Premium service level:
        * If you created an order before the cutoff time, but the pickup end time is less than 90 minutes from now, then the pickup request will fail.
        * If you created an order before the cutoff time, and the pickup end time is 90 minutes or more from now, then the pickup can be created on the same day.
        * If you created an order after the cutoff time, then the pickup will rollover to next working day.

        **Note:** If you're unsure what the cutoff time is, check with your Account Manager.

    Timezone:
      type: string
      enum:
        - Asia/Singapore
        - Asia/Kuala_Lumpur
        - Asia/Jakarta
        - Asia/Jayapura
        - Asia/Makassar
        - Asia/Bangkok
        - Asia/Manila
        - Asia/Ho_Chi_Minh
        - Asia/Yangon
    Dimension:
      type: object
      properties:
        size:
          type: string
          enum:
            - S
            - M
            - L
            - XL
            - XXL
        weight:
          type: number
          description: 'The weight of the parcel, in kilograms (k.g.).'
          format: double
        length:
          type: number
          description: 'The length of the parcel, in centimeters (c.m.).'
          format: double
        width:
          type: number
          description: 'The width of the parcel, in centimeters (c.m.).'
          format: double
        height:
          type: number
          description: 'The height of the parcel, in centimeters (c.m.).'
          format: double
      description: Dimension information of parcels or objects.
    Items:
      required:
        - item_description
      type: object
      properties:
        item_description:
          maxLength: 255
          type: string
          description: The description of the item.
        quantity:
          type: number
          description: Total quantity of the item.
        is_dangerous_good:
          type: boolean
          description: Declaration of dangerous goods for air freight.
      description: Details of the items.
    PickupBooking:
      allOf:
        - $ref: '#/components/schemas/NewPickupBooking'
    NewPickupBooking:
      type: object
      properties:
        pickup_service_type:
          type: string
          description: |-
            Specifies the pickup service type.
          default: Scheduled
        pickup_service_level:
          type: string
          description: |-
            Specifies the pickup service level. You have the option of choosing `Standard` or `Premium` levels.

            Premium pickups have shorter pickup time slots, and the cutoff rules are more relaxed. Check with your Account Manager to see if your account is eligible for Premium pickups.
          default: Standard
          enum:
            - Standard
            - Premium
        pickup_date:
          pattern: '^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))$'
          type: string
          description: |-
            Specifies the date on which the pickup will occur in the `yyyy-MM-dd` format.

            The system will adjust the pickup date automatically to the next available working day, if the specified pickup date:
            * Falls on a blocked date. For example, a public holiday or warehouse closure.
            * Doesn't meet the cutoff time for pickup bookings for that day.

            Any adjustments to the pickup date are returned in the response.
        pickup_timeslot:
          $ref: '#/components/schemas/PickupTimeslotInfo'
        pickup_instructions:
          type: string
        pickup_approximate_volume:
          type: string
          enum:
            - Full-Van Load
            - Half-Van Load
            - Larger than Van Load
            - Less than 10 Parcels
            - Less than 3 Parcels
            - Trolley Required
        pickup_address:
          $ref: '#/components/schemas/NewSender'
        reference:
          type: object
          properties:
            merchant_pickup_booking_number:
              maxLength: 255
              type: string
              description: Identifier of the pickup booking in shipper's systems.
          description: 'Reference information. This information returns to the shipper via webhooks, and can be used as query parameters when retrieving orders from the our API.'
      example:
        pickup_address:
          name: John Doe
          phone_number: '+60138201527'
          email: john.doe@gmail.com
          address:
            address1: 17 Lorong Jambu 3
            address2: ''
            area: Taman Sri Delima
            city: Simpang Ampat
            state: Pulau Pinang
            country: MY
            postcode: '51200'
        pickup_service_type: Scheduled
        pickup_service_level: Standard
        pickup_date: '2021-12-15'
        pickup_timeslot:
          start_time: '09:00'
          end_time: '12:00'
          timezone: Asia/Kuala_Lumpur
        pickup_instructions: Pickup with care!
        pickup_approximate_volume: Less than 3 Parcels
        reference:
          merchant_pickup_booking_number: Pickup-123456
    AccessTokenGrantRequest:
      required:
        - client_id
        - client_secret
        - grant_type
      type: object
      properties:
        client_id:
          type: string
          description: Client ID from your Ninja Dashboard account.
        client_secret:
          type: string
          description: Client Key from your Ninja Dashboard account.
        grant_type:
          type: string
          enum:
            - client_credentials
    AccessTokenGrantResponse:
      required:
        - access_token
        - expires
        - expires_in
        - token_type
      type: object
      properties:
        access_token:
          type: string
          description: Bearer token to be included in the `Authorization` HTTP header for all API requests.
        expires:
          minimum: 1
          type: integer
          description: The epoch timestamp at which the access token expires.
          format: int32
        expires_in:
          minimum: 300
          type: integer
          description: The number of seconds at which the access token expires.
          format: int32
        token_type:
          type: string
          enum:
            - bearer
    ErrorResponse:
      required:
        - error
      type: object
      properties:
        error:
          $ref: '#/components/schemas/Error'
    Error:
      required:
        - request_id
      type: object
      properties:
        code:
          type: string
          description: Internal error reference code.
        request_id:
          type: string
          description: Request identifier for error tracking.
        title:
          type: string
          description: Frontend-friendly error title.
        message:
          type: string
          description: Frontend-friendly error message.
        details:
          type: array
          items:
            $ref: '#/components/schemas/ErrorDetail'
    ErrorDetail:
      required:
        - field
        - message
        - reason
      type: object
      properties:
        reason:
          type: string
        field:
          type: string
        message:
          type: string
    CancelOrderResponse:
      type: object
      properties:
        trackingId:
          type: string
          description: Tracking ID of the order that was cancelled.
        status:
          type: string
          description: Status of the order.
        updatedAt:
          type: string
          description: Date on which the order was cancelled.
          format: datetime
    OpeningTime:
          type: object
          properties:
            day_of_week:
              description: Number of the day starting from "1" for Monday.
              type: integer
              example: "1"
            end_time:
              description: Time at which the Ninja Point closes.
              type: string
              example: "22:00:00"
            start_time:
              description: Time at which the Ninja Point opens for business.
              type: string
              example: "07:00:00"
    DistributionPointV2:
      type: object
      properties:
        address_1:
          description: Unit and block of the Ninja Point's address.
          type: string
          example: "#01-19/20/21 Clifford Centre"
        address_2:
          description: Street address of the Ninja Point.
          type: string
          example: "24 Raffles Place"
        allow_create_pack:
          description: Flag to indicate if the Ninja Point allows "pack" parcel creation.
          type: boolean
          example: false
        allow_create_post:
          description: Flag to indicate if the Ninja Point allows "post" parcel creation.
          type: boolean
          example: false
        allow_customer_return:
          description: Flag to indicate if the Ninja Point allows customer returns.
          type: boolean
          example: false
        allow_shipper_send:
          description: Flag to indicate if the Ninja Point allows shippers to drop-off "send" parcels.
          type: boolean
          example: false
        can_customer_collect:
          description: Flag to indicate if the Ninja Point allows customer collection.
          type: boolean
          example: true
        directions:
          description: Additional description on how to find the Ninja Point.
          type: string
          example: Next to Setish Boutique
        distance:
          description: N/A
          type: number
        dpms_id:
          description: Legacy Ninja Point identifier.
          type: number
          example: 1337
        floor_number:
          description: Floor number of the Ninja Point's address.
          type: string
          example: "01"
        id:
          description: Ninja Van's identifier for the Ninja Point.
          type: integer
          example: 118
        latitude:
          description: Latitude of the Ninja Point's location.
          type: number
          example: 3.009829
        longitude:
          description: Longitude of the Ninja Point's location.
          type: number
          example: 102.770408
        max_parcel_stay_duration:
          description: Maximum number of days a parcel can stay at the Ninja Point.
          type: number
          example: 3
        name:
          description: >
            Descriptive names of the locations easily understood by customers, to be used in the selection dropdown list.
          type: string
          example: "Mr Fix at Bukit Merah Central"
        opening_hours:
          description: Details of the Ninja Point's opening hours.
          type: array
          items:
            $ref: '#/components/schemas/OpeningTime'
        accepted_parcel_sizes:
          description: >
            Accepted parcel sizes for shipper drop-offs at Ninja Points (excluding customer collection service).
          type: array
          items:
            type: string
            enum:
              - XSMALL
              - SMALL
              - MEDIUM
              - LARGE
              - EXTRALARGE
              - XXLARGE
          example: ["XSMALL", "SMALL"]    
        packs_sold_here:
          description: Flag to indicate if Ninja Packs are available for purchase at the Ninja Point.
          type: boolean
          example:
            - true
        partner_id:
          description: Ninja Point partner's identifier.
          type: number
          example: 123
        partner_name:
          description: Ninja Point partner's name.
          type: string
          example: "Antoni's Cafe"
        postal_code:
          description: Postal code of the Ninja Point's address.
          type: string
          example: "678265"
        short_name:
          description: Identifier used by the order creation endpoint to map to the Ninja Point.
          type: string
          example: "IS_UNS"
        type:
          description: Type of the Ninja Point.
          type: string
          enum:
            - BOX
            - SHOP
        unit_number:
          description: Unit number of the Ninja Point's address.
          type: string
          example: "123"
    SendParcelScanResponse:
      required:
        - tracking_id
        - type
        - status
        - granular_status
      type: object
      properties:
        tracking_id:
          type: string
          description: 'Tracking ID of the parcel'
        type:
          type: string
          description: 'Type of parcel (Normal | Return)'
        status:
          type: string
          description: 'Identifies the major milestone for where an order is in the parcel life cycle'
        granular_status:
          type: string
          description: 'Identify more detailed transitions inside the status'
      example:
        tracking_id: NVSGBEDBP03784ADPL
        type: Normal
        status: Completed
        granular_status: Completed
    SendWebhookRequest:
      required:
        - store
        - received_at
        - tracking_ids
      type: object
      properties:
        store:
          type: string
          description: External store ID.
        received_at:
          type: string
          description: Date and time when the parcel is dropped off at the Ninja Point (yyyy-MM-dd HH:mm:ss in UTC).
        tracking_ids:
          type: array
          description: List of tracking IDs to be dropped off.
          items:
            type: string
      example:
        store: PSVKV4M16A
        received_at: 2022-06-01 00:00:00
        tracking_ids: ["NVSGBEDBP12345ADPL", "NVSGBEDBP03784ADPL"]
    SendWebhookResponse:
      required:
        - success
        - error
        - vendor_code
        - store
      type: object
      properties:
        success:
          type: array
          description: List of parcels that were dropped off.
          items:
            $ref: '#/components/schemas/SendWebhookSuccess'
        error:
          type: array
          description: List of tracking IDs that weren't successfully dropped off.
          items:
            type: string
        vendor_code:
          type: string
          description: Unique identifier used by the partner for integration.
        store:
          type: string
          description: External store ID.
      example:
        success: [{"tracking_id": 'NVSGBEDBP03784ADPL', "job_number": 123, "pin": 239812}]
        error: ["NVSGBEDBP12345ADPL"]
        vendor_code: NV
        store: PSVKV4M16A
    SendWebhookSuccess:
      required:
        - tracking_id
        - job_number
        - pin
      type: object
      properties:
        tracking_id:
          type: string
          description: Tracking ID of the parcel.
        job_number:
          type: integer
          description: Ninja Van job number to which the parcel is added.
        pin:
          type: string
          description: Unlock code used by the driver during the parcel handover.
      example:
        tracking_id: NVSGBEDBP03784ADPL
        job_number: 123
        pin: 239812
    DpOrderEventRequest:
      required:
        - tracking_id
        - store_id
      type: object
      properties:
        tracking_id:
          type: string
          description: Tracking ID of the parcel.
        store_id:
          type: string
          description: External store ID.
        received_at:
          type: string
          description: Date and time when the store received the parcel from the driver (yyyy-MM-dd HH:mm:ss in UTC).
        released_at:
          type: string
          description: Date and time when the store released the parcel to the driver or customer (yyyy-MM-dd HH:mm:ss in UTC).
        customer_unlock_code:
          type: string
          description: Code used by the customer to collect the parcel.
    PriceEstimate:
      required:
        - service_level
        - from
        - to
      type: object
      properties:
        weight:
          type: number
          description: Weight of the parcel, in kilograms (k.g.). If a value is not provided or zero is provided, the price will be estimated based on 1 kg instead.
          format: double
        service_level:
          $ref: '#/components/schemas/ServiceLevel'
        from:
          type: object
          description: Zone codes for the origin of the order.
          allOf:
            - $ref: '#/components/schemas/BillingZone'
        to:
          type: object
          description: Zone codes for the destination of the order.
          allOf:
            - $ref: '#/components/schemas/BillingZone'
    BillingZone:
      required:
        - l1_tier_code
        - l2_tier_code
      type: object
      properties:
        l1_tier_code:
          $ref: '#/components/schemas/ZoneTierCode'
        l2_tier_code:
          $ref: '#/components/schemas/ZoneTierCode'
    BillingZoneName:
      required:
        - l1_name
        - l2_name
        - l3_name
      type: object
      properties: 
        l1_name:
          $ref: '#/components/schemas/ZoneTierCode'
        l2_name:
          $ref: '#/components/schemas/ZoneTierCode'
        l3_name:
          $ref: '#/components/schemas/ZoneTierCode'
    ZoneTierCode:
      type: string
      description: Refer to [Zone code mapping](#tag/Reference/Zone-code-mapping)
    SingleTrackingNumberResponse:
      $ref: "#/components/schemas/TrackingEvent"
    MultipleTrackingNumbersResponse:
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/TrackingEvent"
          example:
            - tracking_number: NVSG01NFD000000001
              is_full_history_available: true
              events:
              - shipper_id: 100001
                tracking_number: NVSG01NFD000000001
                shipper_order_ref_no: NVQA-NFD
                timestamp: 2021-12-15T06:22:48+0000
                status: Pending Pickup
                is_parcel_on_rts_leg: false
              - shipper_id: 100001
                tracking_number: NVSG01NFD000000001
                shipper_order_ref_no: NVQA-NFD
                timestamp: 2021-12-15T06:23:20+0000
                status: Arrived at Origin Hub
                is_parcel_on_rts_leg: false
                comments: SG-BUKIT TIMAH-Zone
              - shipper_id: 100001
                tracking_number: NVSG01NFD000000001
                shipper_order_ref_no: NVQA-NFD
                timestamp: 2021-12-15T06:23:40+0000
                status: Delivered
                is_parcel_on_rts_leg: false
              - shipper_id: 100001
                tracking_number: NVSG01NFD000000001
                shipper_order_ref_no: NVQA-NFD
                timestamp: 2021-12-15T06:23:40+0000
                status: Delivered, Received by Customer
                is_parcel_on_rts_leg: false
            - tracking_number: NVSG01NFD000000002
              is_full_history_available: true
              events:
              - shipper_id: 100002
                tracking_number: NVSG01NFD000000002
                shipper_order_ref_no: NVQA-NFD
                timestamp: 2021-12-15T06:22:48+0000
                status: Pending Pickup
                is_parcel_on_rts_leg: false
              - shipper_id: 100002
                tracking_number: NVSG01NFD000000002
                shipper_order_ref_no: NVQA-NFD
                timestamp: 2021-12-15T06:23:20+0000
                status: Arrived at Origin Hub
                is_parcel_on_rts_leg: false
                arrived_at_origin_hub_information:
                  country: Sg
                  city: Marsiling
                  hub: Express
              - shipper_id: 100002
                tracking_number: NVSG01NFD000000002
                shipper_order_ref_no: NVQA-NFD
                timestamp: 2021-12-15T06:23:40+0000
                status: Delivered
                is_parcel_on_rts_leg: false
              - shipper_id: 100002
                tracking_number: NVSG01NFD000000002
                shipper_order_ref_no: NVQA-NFD
                timestamp: 2021-12-15T06:23:40+0000
                status: Delivered, Received by Customer
                is_parcel_on_rts_leg: false
    arrived_at_transit_hub_information:
        description: Information of the transit hub.
        type: object
        properties:
          country:
            type: string
            description: Country of the transit hub.
          city:
            type: string
            description: City of the transit hub.
          hub:
            type: string
            description: Name of the transit hub.
    in_transit_to_next_sorting_hub_information:
        description: Information of the transit hub.
        type: object
        properties:
          country:
            type: string
            description: Country of the transit hub.
          city:
            type: string
            description: City of the transit hub.
          hub:
            type: string
            description: Name of the transit hub.
    arrived_at_destination_hub_information:
        description: Information of the destination hub.
        type: object
        properties:
          country:
            type: string
            description: Country of the destination hub.
          city:
            type: string
            description: City of the destination hub.
          hub:
            type: string
            description: Name of the destination hub.
    TrackingEvent:
      type: object
      required:
        - tracking_number
        - is_full_history_available
        - events
      description: Details of the tracking events.
      properties:
        tracking_number:
          type: string
          pattern: '^([a-zA-Z0-9]+[-])*[a-zA-Z0-9]+$'
        is_full_history_available:
          type: boolean
          description: Determine if full events are available for the tracking number provided.
        events:
          $ref: "#/components/schemas/Event"
      example:
        tracking_number: NVSG01NFD000000001
        is_full_history_available: true
        events:
        - shipper_id: 100001
          tracking_number: NVSG01NFD000000001
          shipper_order_ref_no: NVQA-NFD
          timestamp: 2021-12-15T06:22:48+0000
          status: Pending Pickup
          is_parcel_on_rts_leg: false
        - shipper_id: 100001
          tracking_number: NVSG01NFD000000001
          shipper_order_ref_no: NVQA-NFD
          timestamp: 2021-12-15T06:23:20+0000
          status: Arrived at Origin Hub
          is_parcel_on_rts_leg: false
          comments: SG-BUKIT TIMAH-Zone
        - shipper_id: 100001
          tracking_number: NVSG01NFD000000001
          shipper_order_ref_no: NVQA-NFD
          timestamp: 2021-12-15T06:23:40+0000
          status: Delivered
          is_parcel_on_rts_leg: false
        - shipper_id: 100001
          tracking_number: NVSG01NFD000000001
          shipper_order_ref_no: NVQA-NFD
          timestamp: 2021-12-15T06:23:40+0000
          status: Delivered, Received by Customer
          is_parcel_on_rts_leg: false
    Event:
      type: array
      items:
        type: object
        required:
          - shipper_id
          - tracking_number
          - shipper_order_ref_no
          - status
          - timestamp
          - is_parcel_on_rts_leg
        properties:
          shipper_id:
            type: string
            description: Identifier of the shipper.
          tracking_number:
            type: string
            description: Identifier of the order.
          shipper_order_ref_no:
            type: string
            description: Identifier of the order in shipper's systems.
          timestamp:
            type: string
            description: Timestamp of the event.
          status:
            type: string
            description: Status of the order.
          is_parcel_on_rts_leg:
            type: boolean
            description: Determine if it is a return parcel.
          WebhookV1HubLocation:
            type: string
            description: Country, city and shortname of the inbounded hub.
            pattern: "<hub_country>-<hub_city>-<hub_short_name>"
          WebhookV2HubLocation:
            description: Location of the hub for the respective status.
            oneOf:
              - $ref: "#/components/schemas/arrived_at_transit_hub_information"
              - $ref: "#/components/schemas/in_transit_to_next_sorting_hub_information"
              - $ref: "#/components/schemas/arrived_at_destination_hub_information"
      description: 'Events information.'
