{
  "openapi": "3.1.0",
  "info": {
    "title": "bitbybit Open API — Shipping",
    "description": "Programmatic access to bitbybit shipping: store locations, provider discovery, coverage lookup, fee quoting, and the full shipment lifecycle (create, book, update, cancel, track) across GoSend, KiriminAja, and RideBlitz.",
    "version": "1.0.0",
    "contact": {
      "name": "bitbybit Support",
      "url": "https://bitbybit.studio"
    }
  },
  "servers": [
    {
      "url": "https://api.bitbybit.studio/customer/api/open/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Store Locations",
      "description": "Manage warehouse / pickup origin addresses. Couriers pick up from these locations when a shipment is booked."
    },
    {
      "name": "Providers",
      "description": "Discover connected carriers and the services each carrier offers."
    },
    {
      "name": "Coverage",
      "description": "Look up the courier-specific province / city / district / sub-district dictionaries needed to populate addresses and quote fees."
    },
    {
      "name": "Fee Calculation",
      "description": "Quote shipping fees across carriers using one unified request shape."
    },
    {
      "name": "Shipments",
      "description": "Create, update, cancel, and track shipments. A shipment is always attached to an Order."
    }
  ],
  "paths": {
    "/shipping/store-locations": {
      "get": {
        "operationId": "listStoreLocations",
        "summary": "List store locations",
        "description": "Return a paginated list of pickup-origin locations registered for your company. The location flagged `mainAddress: true` is the merchant default and always sorts first.",
        "tags": [
          "Store Locations"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            },
            "description": "Page number (1-indexed)."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100
            },
            "description": "Results per page (max 100)."
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Case-insensitive substring matched against `locationName`, `city`, `province`, and `address1`."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/StoreLocation"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createStoreLocation",
        "summary": "Create store location",
        "description": "Register a new pickup origin. Set `mainAddress: true` to make this the default; any previous main location will be unset automatically.\n\nFor KiriminAja Express quoting you must also populate `districtId` and `subDistrictId` from `GET /shipping/coverage/districts` and `GET /shipping/coverage/sub-districts`.",
        "tags": [
          "Store Locations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreLocationInput"
              },
              "examples": {
                "warehouseJakarta": {
                  "summary": "Jakarta warehouse with KiriminAja IDs",
                  "value": {
                    "locationName": "Main Warehouse — Jakarta",
                    "phoneNumber": "+6281234567890",
                    "country": "Indonesia",
                    "countryCode": "ID",
                    "province": "DKI Jakarta",
                    "city": "Kota Jakarta Barat",
                    "district": "Tambora",
                    "districtId": 2412,
                    "subDistrict": "Tanah Sereal",
                    "subDistrictId": 33417,
                    "zip": "11210",
                    "address1": "Jl. KH. Moh. Mansyur No. 12",
                    "latitude": -6.1539,
                    "longitude": 106.8054,
                    "mainAddress": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Store location created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreLocation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shipping/store-locations/{id}": {
      "get": {
        "operationId": "getStoreLocation",
        "summary": "Get store location",
        "description": "Retrieve a single store location by ID.",
        "tags": [
          "Store Locations"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Store location ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreLocation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "operationId": "updateStoreLocation",
        "summary": "Update store location",
        "description": "Update one or more fields on a store location. Setting `mainAddress: true` will unset the previous main location for this company.",
        "tags": [
          "Store Locations"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreLocationInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreLocation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteStoreLocation",
        "summary": "Delete store location",
        "description": "Permanently delete a store location. Any shipments that referenced it via `storeLocationId` will have that field cleared (`ON DELETE SET NULL`).",
        "tags": [
          "Store Locations"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted (no content)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shipping/providers": {
      "get": {
        "operationId": "listShippingProviders",
        "summary": "List connected providers",
        "description": "Return all shipping providers the bitbybit platform integrates with, including whether your company has each one connected and the capability set each provider supports. Use this to render a provider picker in your UI.",
        "tags": [
          "Providers"
        ],
        "responses": {
          "200": {
            "description": "Successful response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProviderInfo"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "code": "gosend",
                      "name": "GoSend",
                      "connected": true,
                      "capabilities": [
                        "calculate_fee",
                        "cancel_booking"
                      ],
                      "description": "Gojek instant motorcycle/car delivery within select Indonesian cities. Best for same-day intra-city shipments."
                    },
                    {
                      "code": "kirimin_aja",
                      "name": "KiriminAja",
                      "connected": true,
                      "capabilities": [
                        "calculate_fee",
                        "coverage_lookup",
                        "create_booking",
                        "cancel_booking",
                        "tracking"
                      ],
                      "description": "Aggregator that exposes 12+ Indonesian couriers (JNE, J&T, SiCepat, …) plus on-demand instant via GoSend, Grab Express, Borzo."
                    },
                    {
                      "code": "ride_blitz",
                      "name": "RideBlitz",
                      "connected": false,
                      "capabilities": [
                        "calculate_fee",
                        "create_booking",
                        "cancel_booking",
                        "tracking"
                      ],
                      "description": "Bali- & Java-focused electric-fleet instant courier with hourly SLAs."
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shipping/providers/{provider}/services": {
      "get": {
        "operationId": "getProviderServices",
        "summary": "Get provider services",
        "description": "Return the catalog of services a specific provider offers — for example RideBlitz returns its four `instant_*_hour(s)` tiers, KiriminAja returns the 12 supported couriers plus 3 instant providers, GoSend returns its `Instant` / `SameDay` / `InstantCar` / `SameDayCar` options.\n\nFor KiriminAja the price-time availability is still gated by `POST /shipping/calculate-fee`; this endpoint surfaces the canonical catalog only.",
        "tags": [
          "Providers"
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "gosend",
                "kirimin_aja",
                "ride_blitz"
              ]
            },
            "description": "Provider code. Only the three external providers are supported here."
          }
        ],
        "responses": {
          "200": {
            "description": "Service catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "provider": {
                          "type": "string"
                        },
                        "services": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ProviderService"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shipping/coverage/provinces": {
      "get": {
        "operationId": "getCoverageProvinces",
        "summary": "List provinces (KiriminAja)",
        "description": "Return the KiriminAja provinces dictionary. Use the returned `provinceId` to fetch cities in the next step.\n\nRequires the merchant to have KiriminAja connected in Settings > Integrations.",
        "tags": [
          "Coverage"
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "kirimin_aja"
              ],
              "default": "kirimin_aja"
            },
            "description": "Currently only `kirimin_aja` is supported."
          }
        ],
        "responses": {
          "200": {
            "description": "Provinces list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "provinceId": {
                            "type": "integer"
                          },
                          "provinceName": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "provinceId": 11,
                      "provinceName": "ACEH"
                    },
                    {
                      "provinceId": 31,
                      "provinceName": "DKI JAKARTA"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/IntegrationNotConnected"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shipping/coverage/cities": {
      "get": {
        "operationId": "getCoverageCities",
        "summary": "List cities",
        "description": "Return cities for the chosen provider.\n\n- **KiriminAja** (`provider=kirimin_aja`, requires `provinceId`): returns the KA city dictionary for that province with `cityId` you can feed into `/coverage/districts`.\n- **RideBlitz** (`provider=ride_blitz`): returns the 46 hard-coded allowed pickup cities. `provinceId` is ignored. Use one of these values as `options.pickupCity` when calling `/shipping/calculate-fee`.",
        "tags": [
          "Coverage"
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "kirimin_aja",
                "ride_blitz"
              ],
              "default": "kirimin_aja"
            }
          },
          {
            "name": "provinceId",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Required when `provider=kirimin_aja`. Get it from `GET /shipping/coverage/provinces`."
          }
        ],
        "responses": {
          "200": {
            "description": "Cities list. Shape varies by provider — see examples.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "cityId": {
                            "type": "integer"
                          },
                          "cityName": {
                            "type": "string"
                          },
                          "cityType": {
                            "type": "string"
                          },
                          "provinceId": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "kiriminAja": {
                    "summary": "KiriminAja cities for province 31",
                    "value": {
                      "data": [
                        {
                          "cityId": 3171,
                          "cityName": "Jakarta Pusat",
                          "cityType": "Kota",
                          "provinceId": 31
                        },
                        {
                          "cityId": 3172,
                          "cityName": "Jakarta Utara",
                          "cityType": "Kota",
                          "provinceId": 31
                        }
                      ]
                    }
                  },
                  "rideBlitz": {
                    "summary": "RideBlitz allowed pickup cities (truncated)",
                    "value": {
                      "data": [
                        {
                          "cityName": "Kota Jakarta Barat"
                        },
                        {
                          "cityName": "Kota Jakarta Pusat"
                        },
                        {
                          "cityName": "Kota Bandung"
                        },
                        {
                          "cityName": "Kota Denpasar"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shipping/coverage/districts": {
      "get": {
        "operationId": "getCoverageDistricts",
        "summary": "List districts (KiriminAja)",
        "description": "Return KiriminAja districts (`kecamatan`) for a given city. The returned `districtId` is what you pass as `origin.districtId` / `destination.districtId` to `/shipping/calculate-fee` and as `senderKecamatanId` / `deliveryKecamatanId` on a shipment.",
        "tags": [
          "Coverage"
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "kirimin_aja"
              ],
              "default": "kirimin_aja"
            }
          },
          {
            "name": "cityId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "City ID from `/coverage/cities`."
          }
        ],
        "responses": {
          "200": {
            "description": "Districts list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "districtId": {
                            "type": "integer"
                          },
                          "districtName": {
                            "type": "string"
                          },
                          "cityId": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/IntegrationNotConnected"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shipping/coverage/sub-districts": {
      "get": {
        "operationId": "getCoverageSubDistricts",
        "summary": "List sub-districts (KiriminAja)",
        "description": "Return KiriminAja sub-districts (`kelurahan`) for a given district. Sub-district IDs are needed for `senderKelurahanId` / `deliveryKelurahanId` when booking a KiriminAja Express shipment.",
        "tags": [
          "Coverage"
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "kirimin_aja"
              ],
              "default": "kirimin_aja"
            }
          },
          {
            "name": "districtId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-districts list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "subDistrictId": {
                            "type": "integer"
                          },
                          "subDistrictName": {
                            "type": "string"
                          },
                          "districtId": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/IntegrationNotConnected"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shipping/calculate-fee": {
      "post": {
        "operationId": "calculateShippingFee",
        "summary": "Calculate shipping fee",
        "description": "Quote one or more service options for a given pickup → drop-off route, using a unified request shape regardless of provider.\n\n**Per-provider requirements**\n\n- **`gosend`** — `origin.{lat,lng}` and `destination.{lat,lng}` only. `options.paymentType` defaults to `3` (GoPay corporate). Returns the four GoSend service tiers (`Instant`, `SameDay`, `InstantCar`, `SameDayCar`).\n- **`kirimin_aja`** — choose `options.serviceType`:\n  - `EXPRESS` (default): requires `origin.districtId` + `destination.districtId` (look them up with `/shipping/coverage/districts`). `options.courier` defaults to all 12 supported couriers; pass a subset to narrow the quote. `options.insurance: true` includes insurance pricing.\n  - `INSTANT`: requires geocoded coordinates plus addresses. `options.instantServices` defaults to `['gosend','borzo','grab_express']`; `options.vehicleType` chooses `TWO_WHEEL` (motor) vs `FOUR_WHEEL` (mobil).\n- **`ride_blitz`** — `options.pickupCity` is required and **must** be one of the 46 allowed cities returned by `GET /shipping/coverage/cities?provider=ride_blitz`. `options.rideBlitzServiceTypes` defaults to all four tiers (`instant_1_hour` … `instant_4_hours`); each tier is quoted in parallel. `options.bikeFuelType` toggles `regular` vs `green` (electric).\n\n**Response shape** is the same for every provider — an array of `options` with `serviceCode`, `serviceName`, `price`, `currency`, plus the raw provider payload under `raw` for advanced cases.",
        "tags": [
          "Fee Calculation"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CalculateFeeRequest"
              },
              "examples": {
                "gosend": {
                  "summary": "GoSend — Jakarta intra-city",
                  "value": {
                    "provider": "gosend",
                    "origin": {
                      "lat": -6.2087,
                      "lng": 106.8456
                    },
                    "destination": {
                      "lat": -6.1751,
                      "lng": 106.865
                    },
                    "package": {
                      "weight": 1500
                    }
                  }
                },
                "kiriminAjaExpress": {
                  "summary": "KiriminAja Express — JNE + J&T quote, Jakarta → Bandung",
                  "value": {
                    "provider": "kirimin_aja",
                    "origin": {
                      "lat": -6.2087,
                      "lng": 106.8456,
                      "districtId": 2412
                    },
                    "destination": {
                      "lat": -6.9175,
                      "lng": 107.6191,
                      "districtId": 1981
                    },
                    "package": {
                      "weight": 1500,
                      "length": 30,
                      "width": 20,
                      "height": 10,
                      "itemValue": 250000,
                      "itemQty": 1
                    },
                    "options": {
                      "serviceType": "EXPRESS",
                      "courier": [
                        "jne",
                        "jnt"
                      ],
                      "insurance": true
                    }
                  }
                },
                "kiriminAjaInstant": {
                  "summary": "KiriminAja Instant — Borzo + GoSend quote",
                  "value": {
                    "provider": "kirimin_aja",
                    "origin": {
                      "lat": -6.2087,
                      "lng": 106.8456,
                      "address": "Jl. Sudirman 1"
                    },
                    "destination": {
                      "lat": -6.1751,
                      "lng": 106.865,
                      "address": "Jl. Thamrin 5"
                    },
                    "package": {
                      "weight": 2000,
                      "itemValue": 150000
                    },
                    "options": {
                      "serviceType": "INSTANT",
                      "instantServices": [
                        "gosend",
                        "borzo"
                      ],
                      "vehicleType": "TWO_WHEEL"
                    }
                  }
                },
                "rideBlitzAll": {
                  "summary": "RideBlitz — quote all four tiers, Denpasar",
                  "value": {
                    "provider": "ride_blitz",
                    "origin": {
                      "lat": -8.6705,
                      "lng": 115.2126
                    },
                    "destination": {
                      "lat": -8.6478,
                      "lng": 115.1378
                    },
                    "package": {
                      "weight": 1000
                    },
                    "options": {
                      "pickupCity": "Kota Denpasar",
                      "bikeFuelType": "green"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote successful.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CalculateFeeResponse"
                    }
                  }
                },
                "examples": {
                  "gosendResponse": {
                    "summary": "GoSend response",
                    "value": {
                      "data": {
                        "provider": "gosend",
                        "currency": "IDR",
                        "options": [
                          {
                            "serviceCode": "Instant",
                            "serviceName": "GoSend Instant",
                            "serviceType": "INSTANT",
                            "courierGroup": null,
                            "price": 28000,
                            "currency": "IDR",
                            "estimatedDelivery": null,
                            "raw": {}
                          },
                          {
                            "serviceCode": "SameDay",
                            "serviceName": "GoSend Same Day",
                            "serviceType": "EXPRESS",
                            "courierGroup": null,
                            "price": 18000,
                            "currency": "IDR",
                            "estimatedDelivery": null,
                            "raw": {}
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shipping/shipments": {
      "get": {
        "operationId": "listShipments",
        "summary": "List shipments",
        "description": "Return a paginated list of shipments for your company. Supports filtering by `type` (provider), `status` (any `ShippingStatus` value), `orderId`, and free-text `search` against `awb`, `orderNo`, `trackingUrl`, and recipient name.",
        "tags": [
          "Shipments"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ShippingProvider"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ShippingStatus"
            }
          },
          {
            "name": "orderId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Shipment"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createShipment",
        "summary": "Create shipment",
        "description": "Persist a new shipment attached to an existing Order. The shipment is created with `shippingStatus: awaiting` and is NOT booked with the carrier unless you set `autoBook: true`.\n\n**When to set `autoBook: true`** — only when you already have all sender/delivery/package details and want to immediately request pickup with KiriminAja or RideBlitz. For GoSend the auto-book flag is accepted but no outbound call is made (GoSend booking happens via the Gojek pickup flow in the merchant app).\n\nIf the carrier booking fails, the shipment row is still persisted; the response returns a `400` with both `data` (the persisted row) and `error` (the booking failure reason) so you can decide whether to retry via `POST /shipping/shipments/{id}/cancel` followed by a fresh create.",
        "tags": [
          "Shipments"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateShipmentInput"
              },
              "examples": {
                "kiriminAjaExpress": {
                  "summary": "KiriminAja Express — persist only (autoBook=false)",
                  "value": {
                    "orderId": "ord_2X9q…",
                    "provider": "kirimin_aja",
                    "name": "JNE REG",
                    "fee": 22000,
                    "currency": "IDR",
                    "serviceType": "EXPRESS",
                    "serviceName": "JNE Regular",
                    "selectedExpedition": "jne:reg",
                    "courierGroup": "regular",
                    "storeLocationId": "loc_8…",
                    "sender": {
                      "name": "Main Warehouse",
                      "phone": "+6281234567890",
                      "address": "Jl. KH. Moh. Mansyur No. 12",
                      "coordinates": {
                        "lat": -6.1539,
                        "lng": 106.8054
                      },
                      "city": "Kota Jakarta Barat",
                      "province": "DKI Jakarta",
                      "country": "Indonesia",
                      "zip": "11210",
                      "kecamatan": "Tambora",
                      "kecamatanId": 2412,
                      "kelurahan": "Tanah Sereal",
                      "kelurahanId": 33417
                    },
                    "delivery": {
                      "name": "Andi Wijaya",
                      "phone": "+6285671234567",
                      "address": "Jl. Asia Afrika No. 8",
                      "coordinates": {
                        "lat": -6.9175,
                        "lng": 107.6191
                      },
                      "city": "Kota Bandung",
                      "province": "Jawa Barat",
                      "country": "Indonesia",
                      "zip": "40111",
                      "kecamatan": "Sumur Bandung",
                      "kecamatanId": 1981,
                      "kelurahan": "Braga",
                      "kelurahanId": 27543
                    },
                    "package": {
                      "weight": 1500,
                      "length": 30,
                      "width": 20,
                      "height": 10,
                      "itemName": "Kemeja batik",
                      "itemType": "fashion",
                      "itemValue": 250000,
                      "itemQty": 1
                    },
                    "insurance": false,
                    "packageHandover": "DROPOFF",
                    "autoBook": false
                  }
                },
                "rideBlitzAutoBook": {
                  "summary": "RideBlitz — create and immediately request pickup (autoBook=true)",
                  "value": {
                    "orderId": "ord_3Y…",
                    "provider": "ride_blitz",
                    "name": "RideBlitz Instant 2h",
                    "fee": 35000,
                    "currency": "IDR",
                    "serviceType": "INSTANT",
                    "serviceName": "Instant 2 Hours",
                    "selectedExpedition": "instant_2_hours",
                    "vehicleType": "TWO_WHEEL",
                    "bikeFuelType": "green",
                    "sender": {
                      "name": "Warung Bali",
                      "phone": "+6281200000000",
                      "address": "Jl. Sunset Road 88",
                      "coordinates": {
                        "lat": -8.7202,
                        "lng": 115.1727
                      },
                      "city": "Kota Denpasar",
                      "province": "Bali"
                    },
                    "delivery": {
                      "name": "Maya Putri",
                      "phone": "+6285711112222",
                      "address": "Jl. Hayam Wuruk 12",
                      "coordinates": {
                        "lat": -8.6478,
                        "lng": 115.2186
                      },
                      "city": "Kota Denpasar",
                      "province": "Bali"
                    },
                    "package": {
                      "weight": 1200,
                      "itemName": "Nasi campur (2 box)",
                      "itemValue": 80000,
                      "itemQty": 2
                    },
                    "autoBook": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Shipment created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Shipment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shipping/shipments/{id}": {
      "get": {
        "operationId": "getShipment",
        "summary": "Get shipment",
        "description": "Retrieve one shipment by ID.",
        "tags": [
          "Shipments"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Shipment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateShipment",
        "summary": "Update shipment",
        "description": "Update one or more shipment fields. All body fields are optional — only fields you include are touched.\n\nUse `triggerBooking: true` to request pickup with the carrier after persisting the update. Same behaviour as `POST /shipments` with `autoBook: true` but lets you split persist and book into two calls (useful when collecting recipient details first and confirming pickup later).",
        "tags": [
          "Shipments"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateShipmentInput"
              },
              "examples": {
                "markDelivered": {
                  "summary": "Mark a custom shipment as delivered",
                  "value": {
                    "shippingStatus": "delivered",
                    "awb": "MANUAL-001"
                  }
                },
                "triggerBooking": {
                  "summary": "Request pickup for an existing KA shipment",
                  "value": {
                    "triggerBooking": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Shipment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shipping/shipments/{id}/cancel": {
      "post": {
        "operationId": "cancelShipment",
        "summary": "Cancel shipment",
        "description": "Cancel a shipment.\n\n- **KiriminAja**: requires `awb` on the shipment (already booked). Cancels via `cancelOrderExpress` and sets `shippingStatus: cancelled`.\n- **RideBlitz**: requires `awb`. Cancels via the RideBlitz API and sets `shippingStatus: cancelled_by_business`.\n- **GoSend**: if `orderNo` exists, calls GoSend cancel API; otherwise just updates the local row. Sets `shippingStatus: cancelled`.\n- **custom / jne**: persists the cancellation locally; no outbound call.\n\nProvide a human-readable `reason` to help your team and the carrier understand why.",
        "tags": [
          "Shipments"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Human-readable cancellation reason (max 500 chars)."
                  }
                }
              },
              "example": {
                "reason": "Customer cancelled the order"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Shipment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shipping/shipments/{id}/tracking": {
      "get": {
        "operationId": "getShipmentTracking",
        "summary": "Get shipment tracking",
        "description": "Return the current tracking state for a shipment, including the carrier's raw tracking payload when available.\n\n- For **KiriminAja** Express shipments, calls `trackOrderExpress` and returns the carrier histories under `providerTracking`.\n- For **RideBlitz** shipments with an `awb`, calls `getOrderDetails` and returns the response under `providerTracking`.\n- For shipments not yet booked, `providerTracking` is `null` and only the local fields (`shippingStatus`, `awb`, `trackingUrl`, `driver`) are returned.\n\nFor near-real-time updates prefer subscribing to webhook events (`shipment.booked`, `shipment.updated`, `shipment.cancelled`).",
        "tags": [
          "Shipments"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tracking payload.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShipmentTracking"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "API key for authentication. Create one in Settings > Developer. The key must have the `BITCRM` product scope and an `orders` resource scope; required action depends on the endpoint (`READ`, `WRITE`, or `DELETE`)."
      }
    },
    "schemas": {
      "ShippingProvider": {
        "type": "string",
        "enum": [
          "gosend",
          "kirimin_aja",
          "ride_blitz",
          "jne",
          "custom"
        ],
        "description": "Provider/courier code. `gosend`, `kirimin_aja`, and `ride_blitz` are externally integrated; `jne` is a passthrough placeholder; `custom` is for merchant-defined methods."
      },
      "ShippingStatus": {
        "type": "string",
        "enum": [
          "confirmed",
          "allocated",
          "out_for_pickup",
          "picked",
          "out_for_delivery",
          "on_hold",
          "cancelled",
          "delivered",
          "rejected",
          "no_driver",
          "awaiting",
          "on_the_way_to_pickup",
          "delivering",
          "delivery_completed",
          "canceled",
          "processed_packages",
          "shipped_packages",
          "canceled_packages",
          "finished_packages",
          "returned_packages",
          "return_finished_packages",
          "created",
          "midmile_pickup_done",
          "midmile_pickup_failed",
          "arrived_blitz_hub",
          "allocation_failure",
          "pickup_started",
          "pickup_done",
          "pickup_failed",
          "pickup_failure",
          "dropoff_started",
          "dropoff_done",
          "dropoff_failed",
          "delivery_failure",
          "cancelled_by_blitz",
          "cancelled_by_business",
          "returned_to_business"
        ],
        "description": "Unified shipping status. Some values are provider-specific (e.g. `cancelled_by_blitz` is RideBlitz, `midmile_pickup_done` is KiriminAja Express)."
      },
      "ServiceType": {
        "type": "string",
        "enum": [
          "INSTANT",
          "EXPRESS"
        ],
        "description": "Service category. `INSTANT` = on-demand intra-city; `EXPRESS` = scheduled inter-city."
      },
      "VehicleType": {
        "type": "string",
        "enum": [
          "TWO_WHEEL",
          "FOUR_WHEEL"
        ],
        "description": "Vehicle requested for the pickup. `TWO_WHEEL` = motorcycle, `FOUR_WHEEL` = car."
      },
      "PackageHandover": {
        "type": "string",
        "enum": [
          "PICKUP",
          "DROPOFF"
        ],
        "description": "How the merchant hands the package to the carrier. `PICKUP` = courier picks up from the store, `DROPOFF` = merchant drops off at carrier hub."
      },
      "BikeFuelType": {
        "type": "string",
        "enum": [
          "regular",
          "green"
        ],
        "description": "RideBlitz fuel preference. `green` = electric fleet (lower emissions, may have limited availability)."
      },
      "PaymentStatus": {
        "type": "string",
        "enum": [
          "unpaid",
          "paid",
          "failed"
        ]
      },
      "Coordinates": {
        "type": "object",
        "required": [
          "lat",
          "lng"
        ],
        "properties": {
          "lat": {
            "type": "number",
            "format": "double",
            "minimum": -90,
            "maximum": 90
          },
          "lng": {
            "type": "number",
            "format": "double",
            "minimum": -180,
            "maximum": 180
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "hasNextPage": {
            "type": "boolean"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable code, e.g. `VALIDATION_ERROR`, `NOT_FOUND`, `INTEGRATION_NOT_CONNECTED`, `PROVIDER_ERROR`, `BOOKING_FAILED`."
              },
              "message": {
                "type": "string",
                "description": "Human-readable error message."
              }
            }
          }
        }
      },
      "StoreLocation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "locationName": {
            "type": "string"
          },
          "phoneNumber": {
            "type": "string",
            "nullable": true
          },
          "country": {
            "type": "string",
            "nullable": true
          },
          "countryCode": {
            "type": "string",
            "nullable": true
          },
          "province": {
            "type": "string",
            "nullable": true
          },
          "provinceCode": {
            "type": "string",
            "nullable": true
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "district": {
            "type": "string",
            "nullable": true
          },
          "districtId": {
            "type": "integer",
            "nullable": true,
            "description": "KiriminAja `kecamatan_id` — required when this location is used as origin for KA Express."
          },
          "subDistrict": {
            "type": "string",
            "nullable": true
          },
          "subDistrictId": {
            "type": "integer",
            "nullable": true,
            "description": "KiriminAja `kelurahan_id`."
          },
          "zip": {
            "type": "string",
            "nullable": true
          },
          "address1": {
            "type": "string",
            "nullable": true
          },
          "address2": {
            "type": "string",
            "nullable": true
          },
          "latitude": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "longitude": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "mainAddress": {
            "type": "boolean",
            "description": "Flagged as the company default pickup origin."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "StoreLocationInput": {
        "type": "object",
        "required": [
          "locationName"
        ],
        "properties": {
          "locationName": {
            "type": "string",
            "maxLength": 255
          },
          "phoneNumber": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "countryCode": {
            "type": "string"
          },
          "province": {
            "type": "string"
          },
          "provinceCode": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "district": {
            "type": "string"
          },
          "districtId": {
            "type": "integer"
          },
          "subDistrict": {
            "type": "string"
          },
          "subDistrictId": {
            "type": "integer"
          },
          "zip": {
            "type": "string"
          },
          "address1": {
            "type": "string"
          },
          "address2": {
            "type": "string"
          },
          "latitude": {
            "type": "number",
            "format": "double"
          },
          "longitude": {
            "type": "number",
            "format": "double"
          },
          "mainAddress": {
            "type": "boolean"
          }
        }
      },
      "ProviderInfo": {
        "type": "object",
        "properties": {
          "code": {
            "$ref": "#/components/schemas/ShippingProvider"
          },
          "name": {
            "type": "string"
          },
          "connected": {
            "type": "boolean",
            "description": "`true` when the merchant has connected this provider in Settings > Integrations."
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "calculate_fee",
                "coverage_lookup",
                "create_booking",
                "cancel_booking",
                "tracking"
              ]
            }
          },
          "description": {
            "type": "string"
          }
        }
      },
      "ProviderService": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Service identifier used in `selectedExpedition` / `serviceCode` fields."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "serviceType": {
            "$ref": "#/components/schemas/ServiceType"
          },
          "maxDistanceKm": {
            "type": "integer",
            "description": "RideBlitz only — the upper distance limit for this tier."
          }
        }
      },
      "AddressInput": {
        "type": "object",
        "required": [
          "name",
          "phone",
          "address",
          "coordinates"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "phone": {
            "type": "string",
            "maxLength": 32,
            "description": "E.164 (e.g. `+6281234567890`) recommended; bitbybit normalizes to `62…` for GoSend."
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "address": {
            "type": "string"
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          },
          "city": {
            "type": "string"
          },
          "province": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "zip": {
            "type": "string"
          },
          "kecamatan": {
            "type": "string"
          },
          "kecamatanId": {
            "type": "integer",
            "description": "KiriminAja `kecamatan_id` — required for KA Express."
          },
          "kelurahan": {
            "type": "string"
          },
          "kelurahanId": {
            "type": "integer",
            "description": "KiriminAja `kelurahan_id` — required for KA Express."
          },
          "coordinates": {
            "$ref": "#/components/schemas/Coordinates"
          }
        }
      },
      "AddressOutput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "province": {
            "type": "string",
            "nullable": true
          },
          "country": {
            "type": "string",
            "nullable": true
          },
          "zip": {
            "type": "string",
            "nullable": true
          },
          "kecamatan": {
            "type": "string",
            "nullable": true
          },
          "kecamatanId": {
            "type": "integer",
            "nullable": true
          },
          "kelurahan": {
            "type": "string",
            "nullable": true
          },
          "kelurahanId": {
            "type": "integer",
            "nullable": true
          },
          "coordinates": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Coordinates"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "PackageInput": {
        "type": "object",
        "required": [
          "weight"
        ],
        "properties": {
          "weight": {
            "type": "number",
            "format": "double",
            "description": "Weight in grams."
          },
          "length": {
            "type": "number",
            "format": "double",
            "description": "Length in cm."
          },
          "width": {
            "type": "number",
            "format": "double",
            "description": "Width in cm."
          },
          "height": {
            "type": "number",
            "format": "double",
            "description": "Height in cm."
          },
          "itemName": {
            "type": "string"
          },
          "itemType": {
            "type": "string",
            "description": "KA categories: `fashion`, `peralatan_elektronik_gadget`, `pecah_belah`, `dokumen`, `dokumen_berharga`, etc."
          },
          "itemValue": {
            "type": "number",
            "format": "double",
            "description": "Declared item value in IDR (used for insurance calculations)."
          },
          "itemQty": {
            "type": "integer",
            "minimum": 1
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          }
        }
      },
      "PackageOutput": {
        "type": "object",
        "properties": {
          "weight": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "length": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "width": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "height": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "itemName": {
            "type": "string",
            "nullable": true
          },
          "itemType": {
            "type": "string",
            "nullable": true
          },
          "itemValue": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "itemQty": {
            "type": "integer",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CalculateFeeRequest": {
        "type": "object",
        "required": [
          "provider",
          "origin",
          "destination",
          "package"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "gosend",
              "kirimin_aja",
              "ride_blitz"
            ]
          },
          "origin": {
            "type": "object",
            "required": [
              "lat",
              "lng"
            ],
            "properties": {
              "lat": {
                "type": "number",
                "format": "double"
              },
              "lng": {
                "type": "number",
                "format": "double"
              },
              "address": {
                "type": "string"
              },
              "districtId": {
                "type": "integer",
                "description": "KiriminAja `kecamatan_id` — required for KA Express."
              },
              "subDistrictId": {
                "type": "integer"
              }
            }
          },
          "destination": {
            "type": "object",
            "required": [
              "lat",
              "lng"
            ],
            "properties": {
              "lat": {
                "type": "number",
                "format": "double"
              },
              "lng": {
                "type": "number",
                "format": "double"
              },
              "address": {
                "type": "string"
              },
              "districtId": {
                "type": "integer"
              },
              "subDistrictId": {
                "type": "integer"
              }
            }
          },
          "package": {
            "$ref": "#/components/schemas/PackageInput"
          },
          "options": {
            "type": "object",
            "description": "Per-provider tuning. See endpoint description for which fields apply to which provider.",
            "properties": {
              "courier": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "KiriminAja Express: list of courier codes to quote."
              },
              "instantServices": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "KiriminAja Instant: instant provider codes (`gosend`, `borzo`, `grab_express`)."
              },
              "serviceType": {
                "$ref": "#/components/schemas/ServiceType"
              },
              "insurance": {
                "type": "boolean",
                "description": "KiriminAja: include insurance in the quote."
              },
              "pickupCity": {
                "type": "string",
                "description": "RideBlitz: pickup city (must be from the allowed list)."
              },
              "rideBlitzServiceTypes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "instant_1_hour",
                    "instant_2_hours",
                    "instant_3_hours",
                    "instant_4_hours"
                  ]
                },
                "description": "RideBlitz: tiers to quote. Defaults to all four."
              },
              "bikeFuelType": {
                "$ref": "#/components/schemas/BikeFuelType"
              },
              "paymentType": {
                "type": "integer",
                "description": "GoSend: defaults to `3`."
              },
              "vehicleType": {
                "$ref": "#/components/schemas/VehicleType"
              }
            }
          }
        }
      },
      "CalculateFeeOption": {
        "type": "object",
        "properties": {
          "serviceCode": {
            "type": "string"
          },
          "serviceName": {
            "type": "string"
          },
          "serviceType": {
            "$ref": "#/components/schemas/ServiceType"
          },
          "courierGroup": {
            "type": "string",
            "nullable": true,
            "description": "KiriminAja Express: `regular`, `economy`, `one_day`, `cargo`, `next_day`."
          },
          "price": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "default": "IDR"
          },
          "estimatedDelivery": {
            "type": "string",
            "nullable": true,
            "description": "Human-readable ETA when the provider returns one (e.g. `1-2 days`, `2 hours`)."
          },
          "available": {
            "type": "boolean",
            "description": "RideBlitz only — `false` when a tier failed to quote (out of range, etc.)."
          },
          "raw": {
            "type": "object",
            "description": "Pass-through of the provider's raw option payload for advanced consumers.",
            "additionalProperties": true
          }
        }
      },
      "CalculateFeeResponse": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "gosend",
              "kirimin_aja",
              "ride_blitz"
            ]
          },
          "currency": {
            "type": "string"
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CalculateFeeOption"
            }
          }
        }
      },
      "CreateShipmentInput": {
        "type": "object",
        "required": [
          "orderId",
          "provider",
          "name",
          "fee",
          "sender",
          "delivery",
          "package"
        ],
        "properties": {
          "orderId": {
            "type": "string",
            "description": "ID of an Order in this company. Required — every shipment is attached to an order."
          },
          "provider": {
            "$ref": "#/components/schemas/ShippingProvider"
          },
          "name": {
            "type": "string",
            "maxLength": 255,
            "description": "Human-readable label, e.g. `JNE REG` or `GoSend Instant`."
          },
          "fee": {
            "type": "number",
            "format": "double",
            "minimum": 0,
            "description": "Shipping cost in the smallest unit of `currency` (e.g. `22000` = IDR 22,000)."
          },
          "currency": {
            "type": "string",
            "default": "IDR"
          },
          "serviceType": {
            "$ref": "#/components/schemas/ServiceType"
          },
          "serviceName": {
            "type": "string"
          },
          "selectedExpedition": {
            "type": "string",
            "description": "Provider service identifier, e.g. `jne:reg`, `instant_2_hours`, `Instant`."
          },
          "selectedDelivery": {
            "type": "string"
          },
          "courierGroup": {
            "type": "string",
            "description": "KiriminAja Express group: `regular`, `economy`, `one_day`, `cargo`, `next_day`."
          },
          "vehicleType": {
            "$ref": "#/components/schemas/VehicleType"
          },
          "bikeFuelType": {
            "$ref": "#/components/schemas/BikeFuelType"
          },
          "packageHandover": {
            "$ref": "#/components/schemas/PackageHandover"
          },
          "pickupSchedule": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 timestamp (with offset) when KiriminAja Express should pick up. Required when `packageHandover=PICKUP` for KA."
          },
          "storeLocationId": {
            "type": "string",
            "description": "Optional — link this shipment to a saved store location for analytics + future booking lookups."
          },
          "sender": {
            "$ref": "#/components/schemas/AddressInput"
          },
          "delivery": {
            "$ref": "#/components/schemas/AddressInput"
          },
          "package": {
            "$ref": "#/components/schemas/PackageInput"
          },
          "insurance": {
            "type": "boolean"
          },
          "insuranceFee": {
            "type": "number",
            "format": "double",
            "minimum": 0
          },
          "insuranceAmount": {
            "type": "number",
            "format": "double",
            "minimum": 0
          },
          "discountAmount": {
            "type": "number",
            "format": "double",
            "minimum": 0
          },
          "discountPercentage": {
            "type": "number",
            "format": "double",
            "minimum": 0,
            "maximum": 100
          },
          "distance": {
            "type": "number",
            "format": "double",
            "minimum": 0,
            "description": "Pickup → delivery distance in km."
          },
          "autoBook": {
            "type": "boolean",
            "default": false,
            "description": "When `true`, immediately request pickup with the carrier after persisting. Supported for `kirimin_aja` and `ride_blitz`."
          }
        }
      },
      "UpdateShipmentInput": {
        "type": "object",
        "description": "All fields optional — patch semantics.",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "fee": {
            "type": "number",
            "format": "double",
            "minimum": 0
          },
          "shippingStatus": {
            "$ref": "#/components/schemas/ShippingStatus"
          },
          "serviceName": {
            "type": "string"
          },
          "serviceType": {
            "$ref": "#/components/schemas/ServiceType"
          },
          "selectedExpedition": {
            "type": "string"
          },
          "selectedDelivery": {
            "type": "string"
          },
          "courierGroup": {
            "type": "string"
          },
          "vehicleType": {
            "$ref": "#/components/schemas/VehicleType"
          },
          "bikeFuelType": {
            "$ref": "#/components/schemas/BikeFuelType"
          },
          "packageHandover": {
            "$ref": "#/components/schemas/PackageHandover"
          },
          "pickupSchedule": {
            "type": "string",
            "format": "date-time"
          },
          "sender": {
            "$ref": "#/components/schemas/AddressInput"
          },
          "delivery": {
            "$ref": "#/components/schemas/AddressInput"
          },
          "package": {
            "$ref": "#/components/schemas/PackageInput"
          },
          "insurance": {
            "type": "boolean"
          },
          "insuranceFee": {
            "type": "number",
            "format": "double",
            "minimum": 0
          },
          "insuranceAmount": {
            "type": "number",
            "format": "double",
            "minimum": 0
          },
          "discountAmount": {
            "type": "number",
            "format": "double",
            "minimum": 0
          },
          "discountPercentage": {
            "type": "number",
            "format": "double",
            "minimum": 0,
            "maximum": 100
          },
          "distance": {
            "type": "number",
            "format": "double",
            "minimum": 0
          },
          "trackingUrl": {
            "type": "string",
            "format": "uri"
          },
          "awb": {
            "type": "string"
          },
          "orderNo": {
            "type": "string"
          },
          "bookingId": {
            "type": "string"
          },
          "pickupNumber": {
            "type": "string"
          },
          "paymentId": {
            "type": "string"
          },
          "paymentStatus": {
            "$ref": "#/components/schemas/PaymentStatus"
          },
          "statusCode": {
            "type": "integer"
          },
          "cancellationReason": {
            "type": "string"
          },
          "triggerBooking": {
            "type": "boolean",
            "description": "When `true`, request pickup with the carrier after persisting this update. Same outcome as creating the shipment with `autoBook: true`."
          }
        }
      },
      "Shipment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "orderId": {
            "type": "string",
            "nullable": true
          },
          "provider": {
            "$ref": "#/components/schemas/ShippingProvider"
          },
          "providerLabel": {
            "type": "string",
            "description": "Human-readable provider name (e.g. `KiriminAja`)."
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "fee": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "shippingStatus": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ShippingStatus"
              },
              {
                "type": "null"
              }
            ]
          },
          "serviceType": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ServiceType"
              },
              {
                "type": "null"
              }
            ]
          },
          "serviceName": {
            "type": "string",
            "nullable": true
          },
          "selectedExpedition": {
            "type": "string",
            "nullable": true
          },
          "selectedDelivery": {
            "type": "string",
            "nullable": true
          },
          "courierGroup": {
            "type": "string",
            "nullable": true
          },
          "vehicleType": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/VehicleType"
              },
              {
                "type": "null"
              }
            ]
          },
          "bikeFuelType": {
            "type": "string",
            "nullable": true
          },
          "packageHandover": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PackageHandover"
              },
              {
                "type": "null"
              }
            ]
          },
          "pickupSchedule": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "storeLocationId": {
            "type": "string",
            "nullable": true
          },
          "sender": {
            "$ref": "#/components/schemas/AddressOutput"
          },
          "delivery": {
            "$ref": "#/components/schemas/AddressOutput"
          },
          "package": {
            "$ref": "#/components/schemas/PackageOutput"
          },
          "driver": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "nullable": true
                  },
                  "phone": {
                    "type": "string",
                    "nullable": true
                  },
                  "photo": {
                    "type": "string",
                    "nullable": true
                  },
                  "licensePlate": {
                    "type": "string",
                    "nullable": true
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "insurance": {
            "type": "boolean",
            "nullable": true
          },
          "insuranceFee": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "insuranceAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "discountAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "discountPercentage": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "distance": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "trackingUrl": {
            "type": "string",
            "nullable": true
          },
          "awb": {
            "type": "string",
            "nullable": true,
            "description": "Carrier waybill / tracking number. Populated after booking."
          },
          "orderNo": {
            "type": "string",
            "nullable": true,
            "description": "Carrier order number (KA `kj_order_id`, GoSend `orderNo`, RB merchant order id)."
          },
          "bookingId": {
            "type": "string",
            "nullable": true
          },
          "pickupNumber": {
            "type": "string",
            "nullable": true,
            "description": "KiriminAja Express pickup batch number."
          },
          "paymentId": {
            "type": "string",
            "nullable": true
          },
          "paymentStatus": {
            "type": "string",
            "nullable": true
          },
          "statusCode": {
            "type": "integer",
            "nullable": true,
            "description": "Provider-specific status code."
          },
          "cancellationReason": {
            "type": "string",
            "nullable": true
          },
          "cancelledBy": {
            "type": "string",
            "nullable": true
          },
          "cancelledSource": {
            "type": "string",
            "nullable": true
          },
          "eventTimestamp": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ShipmentTracking": {
        "type": "object",
        "properties": {
          "shipmentId": {
            "type": "string"
          },
          "provider": {
            "$ref": "#/components/schemas/ShippingProvider"
          },
          "shippingStatus": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ShippingStatus"
              },
              {
                "type": "null"
              }
            ]
          },
          "awb": {
            "type": "string",
            "nullable": true
          },
          "orderNo": {
            "type": "string",
            "nullable": true
          },
          "trackingUrl": {
            "type": "string",
            "nullable": true
          },
          "driver": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "nullable": true
                  },
                  "phone": {
                    "type": "string",
                    "nullable": true
                  },
                  "photo": {
                    "type": "string",
                    "nullable": true
                  },
                  "licensePlate": {
                    "type": "string",
                    "nullable": true
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "eventTimestamp": {
            "type": "string",
            "nullable": true
          },
          "providerTracking": {
            "description": "Raw provider tracking payload. Shape varies by provider; `null` when not yet booked.",
            "anyOf": [
              {
                "type": "object",
                "additionalProperties": true
              },
              {
                "type": "null"
              }
            ]
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "UNAUTHORIZED",
                "message": "Invalid API key"
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "NOT_FOUND",
                "message": "Shipment not found"
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Request validation failed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "VALIDATION_ERROR",
                "message": "origin.districtId is required for KiriminAja Express"
              }
            }
          }
        }
      },
      "IntegrationNotConnected": {
        "description": "The required courier integration is not connected for this company.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "INTEGRATION_NOT_CONNECTED",
                "message": "KiriminAja is not connected. Connect KiriminAja in Settings > Integrations first."
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "RATE_LIMITED",
                "message": "Too many requests"
              }
            }
          }
        }
      }
    }
  }
}