{
  "openapi": "3.1.0",
  "info": {
    "title": "Bento partner API",
    "version": "1.0.0",
    "description": "Bento Delivaz partner API v1. Select the sandbox, production or local server. Amounts are integer NGN kobo; timestamps are RFC3339 UTC. Credentials are issued separately during onboarding."
  },
  "servers": [
    {
      "url": "https://api.bentodelivaz.com/sandbox/v1",
      "description": "Sandbox"
    },
    {
      "url": "https://api.bentodelivaz.com/prod/v1",
      "description": "Production"
    },
    {
      "url": "http://bentoadmin.test/api/partner/v1",
      "description": "Local testing only"
    }
  ],
  "security": [
    {
      "partnerBearer": []
    }
  ],
  "paths": {
    "/quotes": {
      "post": {
        "operationId": "createQuote",
        "summary": "Calculate a server-routed expiring B2B quote",
        "description": "Required scope: `quotes:create`.",
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "400": {
            "description": "HTTPS required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Credential invalid, revoked or expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or booking restriction",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Missing or foreign resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid shipment/quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Unavailable dependency or disabled API",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "shipment"
                ],
                "properties": {
                  "shipment": {
                    "$ref": "#/components/schemas/Shipment"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/deliveries": {
      "post": {
        "operationId": "createDelivery",
        "summary": "Book once from a quote; wait for readiness",
        "description": "Required scope: `deliveries:create`.",
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Delivery"
                }
              }
            }
          },
          "400": {
            "description": "HTTPS required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Credential invalid, revoked or expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or booking restriction",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Missing or foreign resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid shipment/quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Unavailable dependency or disabled API",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "quote_id",
                  "external_reference",
                  "shipment"
                ],
                "properties": {
                  "quote_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "external_reference": {
                    "type": "string",
                    "maxLength": 128
                  },
                  "shipment": {
                    "$ref": "#/components/schemas/BookingShipment"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            },
            "description": "Same key and same body returns original creation response; changed body conflicts."
          }
        ]
      }
    },
    "/deliveries/{id}/ready": {
      "post": {
        "operationId": "readyDelivery",
        "summary": "Signal vendor readiness and initiate dispatch idempotently",
        "description": "Required scope: `deliveries:ready`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Delivery"
                }
              }
            }
          },
          "400": {
            "description": "HTTPS required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Credential invalid, revoked or expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or booking restriction",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Missing or foreign resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid shipment/quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Unavailable dependency or disabled API",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/deliveries/{id}": {
      "get": {
        "operationId": "getDelivery",
        "summary": "Retrieve the current owned delivery snapshot",
        "description": "Required scope: `deliveries:read`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Delivery"
                }
              }
            }
          },
          "400": {
            "description": "HTTPS required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Credential invalid, revoked or expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or booking restriction",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Missing or foreign resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid shipment/quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Unavailable dependency or disabled API",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/deliveries/{id}/location": {
      "get": {
        "operationId": "getDeliveryLocation",
        "summary": "Retrieve the current assigned rider last-reported position",
        "description": "Required scope: `location:read`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Location"
                }
              }
            }
          },
          "400": {
            "description": "HTTPS required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Credential invalid, revoked or expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or booking restriction",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Missing or foreign resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid shipment/quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Unavailable dependency or disabled API",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "partnerBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Server-side scoped partner key; separate from webhook signing secret."
      }
    },
    "schemas": {
      "Address": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "address",
          "latitude",
          "longitude"
        ],
        "properties": {
          "address": {
            "type": "string",
            "maxLength": 500
          },
          "latitude": {
            "type": "number",
            "minimum": -90,
            "maximum": 90
          },
          "longitude": {
            "type": "number",
            "minimum": -180,
            "maximum": 180
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "phone": {
            "type": "string",
            "maxLength": 32
          }
        }
      },
      "BookingAddress": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "address",
          "latitude",
          "longitude",
          "name",
          "phone"
        ],
        "properties": {
          "address": {
            "type": "string",
            "maxLength": 500
          },
          "latitude": {
            "type": "number",
            "minimum": -90,
            "maximum": 90
          },
          "longitude": {
            "type": "number",
            "minimum": -180,
            "maximum": 180
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "phone": {
            "type": "string",
            "maxLength": 32
          }
        }
      },
      "Parcel": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "category_id": {
            "type": "integer",
            "minimum": 1
          },
          "weight_kg": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "length_cm": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "width_cm": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "height_cm": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "instructions": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1000
          }
        }
      },
      "Shipment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "pickup",
          "recipient",
          "parcel"
        ],
        "properties": {
          "pickup": {
            "$ref": "#/components/schemas/Address"
          },
          "recipient": {
            "$ref": "#/components/schemas/Address"
          },
          "parcel": {
            "$ref": "#/components/schemas/Parcel"
          }
        }
      },
      "BookingShipment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "pickup",
          "recipient",
          "parcel"
        ],
        "properties": {
          "pickup": {
            "$ref": "#/components/schemas/BookingAddress"
          },
          "recipient": {
            "$ref": "#/components/schemas/BookingAddress"
          },
          "parcel": {
            "$ref": "#/components/schemas/Parcel"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "request_id"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "Quote": {
        "type": "object",
        "required": [
          "id",
          "distance_metres",
          "amount_kobo",
          "currency",
          "tariff_version",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "distance_metres": {
            "type": "integer",
            "minimum": 0
          },
          "amount_kobo": {
            "type": "integer",
            "minimum": 0
          },
          "currency": {
            "const": "NGN"
          },
          "tariff_version": {
            "type": "string"
          },
          "breakdown": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "amount_kobo": {
                  "type": "integer"
                }
              }
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Delivery": {
        "type": "object",
        "required": [
          "id",
          "external_reference",
          "status",
          "readiness_status",
          "amount_kobo",
          "currency",
          "version"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "external_reference": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "accepted",
              "assigned",
              "in_transit",
              "delivered",
              "canceled",
              "dispatch_failed"
            ]
          },
          "readiness_status": {
            "type": "string",
            "enum": [
              "awaiting_ready",
              "ready"
            ]
          },
          "amount_kobo": {
            "type": "integer"
          },
          "currency": {
            "const": "NGN"
          },
          "tariff_version": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ready_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "assigned_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "picked_up_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "canceled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "failure_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "proof_of_delivery": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "method": {
                "const": "otp"
              },
              "verified": {
                "type": "boolean"
              },
              "verified_at": {
                "type": "string",
                "format": "date-time"
              },
              "photo_available": {
                "type": "boolean"
              }
            }
          },
          "lookup_url": {
            "type": "string"
          }
        }
      },
      "Location": {
        "type": "object",
        "required": [
          "delivery_id",
          "availability",
          "location",
          "as_of",
          "age_seconds",
          "is_stale",
          "reason"
        ],
        "properties": {
          "delivery_id": {
            "type": "string",
            "format": "uuid"
          },
          "availability": {
            "enum": [
              "available",
              "not_available",
              "tracking_ended"
            ]
          },
          "location": {
            "type": [
              "object",
              "null"
            ],
            "required": [
              "latitude",
              "longitude",
              "observed_at"
            ],
            "properties": {
              "latitude": {
                "type": "number"
              },
              "longitude": {
                "type": "number"
              },
              "observed_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "as_of": {
            "type": "string",
            "format": "date-time"
          },
          "age_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "is_stale": {
            "type": "boolean"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "DeliveryEvent": {
        "type": "object",
        "required": [
          "event_id",
          "type",
          "delivery_id",
          "external_reference",
          "version",
          "occurred_at",
          "status",
          "readiness_status",
          "picked_up_at"
        ],
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "enum": [
              "delivery.accepted",
              "delivery.ready",
              "delivery.assigned",
              "delivery.in_transit",
              "delivery.delivered",
              "delivery.canceled",
              "delivery.dispatch_failed"
            ]
          },
          "delivery_id": {
            "type": "string",
            "format": "uuid"
          },
          "external_reference": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "accepted",
              "assigned",
              "in_transit",
              "delivered",
              "canceled",
              "dispatch_failed"
            ]
          },
          "readiness_status": {
            "type": "string",
            "enum": [
              "awaiting_ready",
              "ready"
            ]
          },
          "picked_up_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ready_at": {
            "type": "string",
            "format": "date-time"
          },
          "milestone": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "pod": {
            "type": "object",
            "properties": {
              "method": {
                "const": "otp"
              },
              "verified_at": {
                "type": "string",
                "format": "date-time"
              },
              "photo_count": {
                "type": "integer",
                "minimum": 0
              }
            }
          }
        }
      },
      "ConnectionTest": {
        "type": "object",
        "required": [
          "event_id",
          "type",
          "occurred_at",
          "test"
        ],
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "const": "connection_test"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "test": {
            "const": true
          }
        }
      }
    }
  },
  "webhooks": {
    "deliveryEvent": {
      "post": {
        "operationId": "receiveDeliveryEvent",
        "summary": "Bento posts signed status events to the provisioned MFG HTTPS callback",
        "security": [],
        "description": "At least once; duplicates and out-of-order versions possible. Verify HMAC-SHA256(timestamp + dot + exact raw body) using the key-ID secret before processing. Return 2xx only after durable acceptance. Signature timestamp changes on retry; event identity and occurrence time do not. See integration-guide.md and verify-webhook.js.",
        "parameters": [
          {
            "name": "X-Bento-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stable UUID; deduplicate"
          },
          {
            "name": "X-Bento-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unix seconds at signing time"
          },
          {
            "name": "X-Bento-Key-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Select provisioned verification secret"
          },
          {
            "name": "X-Bento-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "sha256= followed by lowercase hexadecimal HMAC"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DeliveryEvent"
                  },
                  {
                    "$ref": "#/components/schemas/ConnectionTest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Example successful acknowledgement; any 2xx is accepted"
          },
          "default": {
            "description": "Non-2xx/timeout triggers bounded retry; exhausted attempts require Bento Admin replay"
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Public integration reference",
    "url": "https://api.bentodelivaz.com/docs"
  }
}
