# Documentation generated by ElektraOS.dev | Yossra Benzad | Claude Code
# Source: MealShift-API-documentation-04.06.2025.pdf
openapi: 3.0.3
info:
  title: MealShift API
  version: "2.0"
  x-generator: ElektraOS.dev
  x-author: Yossra Benzad
  description: |
    # Overview

    MealShift is an on-demand delivery platform API designed for POS systems, aggregators, and restaurant groups operating in the UK. This documentation covers the full integration lifecycle - from credential generation to live order tracking via webhooks.

    ## Base URL

    All API requests are made to:

    ```
    https://api.mealshift.co.uk
    ```

    ## Getting Started

    Integrating with MealShift follows a simple flow:

    1. **Generate credentials** - Call the access endpoint with your partner slug to receive your `ms-partner-id` and `secret`.
    2. **Obtain a token** - Exchange your `secret` for a short-lived Bearer token via the auth endpoint.
    3. **Register clients** - Register each restaurant or venue that will dispatch deliveries through MealShift.
    4. **Request quotes** - Before publishing an order, request a delivery quote to get pricing and distance.
    5. **Publish orders** - Submit the order for dispatch. MealShift assigns a driver automatically.
    6. **Track via webhooks** - Receive real-time status updates and cancellation events at your configured webhook URLs.

    ## Rate Limits

    The API enforces rate limits per partner credential. If you receive a `429 Too Many Requests` response, back off and retry with exponential delay. Contact support if you need higher throughput.

    ## Errors

    All error responses follow a consistent shape:

    ```json
    {
      "error": "short_code",
      "message": "Human-readable description of what went wrong."
    }
    ```

    Common HTTP status codes:

    | Code | Meaning |
    |------|---------|
    | 400  | Bad request - check your payload |
    | 401  | Unauthorized - invalid or expired token |
    | 403  | Forbidden - missing ms-partner-id header |
    | 404  | Resource not found |
    | 409  | Conflict - duplicate reference |
    | 429  | Rate limit exceeded |
    | 500  | Internal server error |

    # Authentication

    MealShift uses a two-layer authentication model:

    1. **Partner ID** (`ms-partner-id` header) - Identifies your integration. Included in every request.
    2. **Bearer Token** (`Authorization: Bearer <token>` header) - Short-lived access token obtained by exchanging your `secret`. Required for all endpoints except credential generation.

    ### Token Lifecycle

    Tokens expire after the number of seconds indicated in the `expiresIn` field of the auth response. Your integration should cache the token and refresh it before expiry. Do not generate a new token on every request.

    ```
    Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
    ms-partner-id: 64a1b2c3d4e5f6a7b8c9d0e1
    ```

    # Webhooks

    MealShift pushes real-time events to your configured webhook URLs. You configure these URLs when generating credentials or via the update-configuration endpoint.

    ## Event Types

    | Event | Webhook Field | Description |
    |-------|--------------|-------------|
    | Order status updated | `updatesWebhook` | Fired when order status changes (NEW, PICKING_UP, IN_PROGRESS, DONE) |
    | Order canceled | `cancellationsWebhook` | Fired when an order is canceled by driver, system, or client |

    > **Note:** The `driver` field in status update webhooks is `null` until a driver accepts the delivery (typically from `PICKING_UP` status onward). Always handle the case where `driver` is absent.

    ## Delivery

    - MealShift sends a POST request with a JSON body to your webhook URL.
    - Your endpoint must respond with a `2xx` status within 10 seconds.
    - Failed deliveries are retried up to 5 times with exponential backoff.

    ## Security

    Validate incoming webhooks by checking that the `clientReference` matches a known client in your system. We recommend allowlisting MealShift IP ranges in production.

  contact:
    name: MealShift Support
    url: https://mealshift.co.uk

x-tagGroups:
  - name: Getting Started
    tags:
      - Access
      - Authentication
  - name: Core API
    tags:
      - Clients
      - Quotes
      - Orders
  - name: Events
    tags:
      - Webhooks

tags:
  - name: Access
    description: Generate and manage your API credentials and webhook configuration.
  - name: Authentication
    description: Obtain and refresh Bearer tokens for API access.
  - name: Clients
    description: Register restaurant clients that will dispatch deliveries through MealShift.
  - name: Quotes
    description: Request delivery price quotes before publishing orders.
  - name: Orders
    description: Publish, track, and cancel delivery orders.
  - name: Webhooks
    description: |
      Real-time event payloads pushed to your configured webhook URLs.

      These are not callable endpoints - they describe the shape of POST requests MealShift sends to your server.

servers:
  - url: https://api.mealshift.co.uk
    description: Production

components:
  securitySchemes:
    partnerId:
      type: apiKey
      in: header
      name: ms-partner-id
      description: Your unique partner identifier, returned when generating credentials.
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Short-lived access token obtained from the auth endpoint.

  parameters:
    partnerPath:
      name: partner
      in: path
      required: true
      schema:
        type: string
      description: Your partner slug (e.g. "my-pos-system").
    partnerIdHeader:
      name: ms-partner-id
      in: header
      required: true
      schema:
        type: string
      description: Your partner identifier.
    partnerTokenHeader:
      name: ms-partner-token
      in: header
      required: true
      schema:
        type: string
      description: Alias for the Bearer token, passed as a header.

  schemas:
    ErrorResponse:
      type: object
      description: Standard error response returned by all endpoints on failure.
      properties:
        error:
          type: string
          description: Machine-readable error code.
          example: "validation_error"
        message:
          type: string
          description: Human-readable description of the error.
          example: "The clientReference field is required."
      required:
        - error
        - message
    Location:
      type: object
      description: A geographic location with address details.
      properties:
        lat:
          type: number
          format: float
          example: 51.5074
          description: Latitude coordinate.
        lng:
          type: number
          format: float
          example: -0.1278
          description: Longitude coordinate.
        country:
          type: string
          example: "GB"
          description: ISO 3166-1 alpha-2 country code.
        city:
          type: string
          example: "London"
        street:
          type: string
          example: "Baker Street"
        streetNumber:
          type: string
          example: "221B"
        flatNumber:
          type: string
          example: "Flat 3"
          description: Apartment or unit number (optional).
        address:
          type: string
          example: "221B Baker Street, London"
          description: Full formatted address string.

    PickupPoint:
      type: object
      description: Pickup location details. Provide either a full address or coordinates.
      properties:
        lat:
          type: number
          format: float
          example: 51.5155
        lng:
          type: number
          format: float
          example: -0.1419
        postcode:
          type: string
          example: "W1U 6SG"
        country:
          type: string
          example: "GB"
        city:
          type: string
          example: "London"
        street:
          type: string
          example: "Marylebone High Street"
        streetNumber:
          type: string
          example: "45"
        flatNumber:
          type: string
          example: ""
        fullAddress:
          type: string
          example: "45 Marylebone High Street, London W1U 6SG"

    Price:
      type: object
      description: Monetary amount with currency.
      required:
        - amount
        - currency
        - formatted
      properties:
        amount:
          type: integer
          description: Amount in the smallest currency unit (e.g. pence for GBP).
          example: 599
        currency:
          type: string
          description: ISO 4217 currency code.
          example: "GBP"
        formatted:
          type: string
          description: Human-readable formatted price.
          example: "5.99 GBP"

    Distance:
      type: object
      description: Distance measurement in both imperial and metric.
      properties:
        meters:
          type: integer
          example: 3200
          description: Distance in meters.
        miles:
          type: number
          format: float
          example: 1.99
          description: Distance in miles.
        estimate:
          type: integer
          description: Estimated travel time in seconds.
          example: 720
        formattedEstimate:
          type: string
          description: Human-readable travel time estimate.
          example: "12 min"

    Driver:
      type: object
      nullable: true
      description: Assigned driver details. Null if no driver is assigned yet.
      properties:
        name:
          type: string
          example: "Alex M."
        phone:
          type: string
          example: "+447700900123"
        transport:
          type: string
          enum:
            - BIKE
            - SCOOTER
            - CAR
          description: Driver vehicle type.
        lat:
          type: number
          format: float
          example: 51.5080
          description: Driver current latitude.
        lng:
          type: number
          format: float
          example: -0.1281
          description: Driver current longitude.

    OrderStatus:
      type: string
      enum:
        - NEW
        - PICKING_UP
        - IN_PROGRESS
        - DONE
        - CANCELED
      description: |
        Current order status:
        - `NEW` - Order received, awaiting driver assignment
        - `PICKING_UP` - Driver en route to pickup location
        - `IN_PROGRESS` - Driver picked up and heading to dropoff
        - `DONE` - Delivery completed
        - `CANCELED` - Order was canceled

    PaymentType:
      type: string
      enum:
        - CASH
        - CARD
        - PAID
        - ONLINE
      description: |
        Payment method for the order:
        - `CASH` - Customer pays driver in cash on delivery
        - `CARD` - Customer pays driver by card on delivery
        - `PAID` - Already paid through the restaurant
        - `ONLINE` - Paid online through the ordering platform

    CredentialsResponse:
      type: object
      description: API credentials and webhook configuration.
      properties:
        id:
          type: string
          description: Your ms-partner-id value.
          example: "64a1b2c3d4e5f6a7b8c9d0e1"
        secret:
          type: string
          description: Secret key used to generate Bearer tokens.
          example: "sk_live_abc123def456..."
        active:
          type: boolean
          description: Whether these credentials are currently active.
          example: true
        updatesWebhook:
          type: string
          nullable: true
          description: URL that receives order status update events.
          example: "https://yourapp.com/webhooks/mealshift/updates"
        cancellationsWebhook:
          type: string
          nullable: true
          description: URL that receives order cancellation events.
          example: "https://yourapp.com/webhooks/mealshift/cancellations"

    WebhookConfig:
      type: object
      description: Webhook URL configuration.
      properties:
        updatesWebhook:
          type: string
          nullable: true
          description: URL to receive order status updates.
          example: "https://yourapp.com/webhooks/mealshift/updates"
        cancellationsWebhook:
          type: string
          nullable: true
          description: URL to receive order cancellation events.
          example: "https://yourapp.com/webhooks/mealshift/cancellations"

    ClientRegistration:
      type: object
      required:
        - clientReference
        - name
        - lastname
        - clientPhone
        - managerPhone
        - organizationName
        - address
      properties:
        clientReference:
          type: string
          description: Your internal reference for this client. Must be unique per partner.
          example: "rest-001"
        email:
          type: string
          format: email
          description: Contact email for the client (optional).
          example: "manager@restaurant.co.uk"
        name:
          type: string
          description: Client contact first name.
          example: "James"
        lastname:
          type: string
          description: Client contact last name.
          example: "Wilson"
        clientPhone:
          type: string
          description: Client phone number (used for delivery coordination).
          example: "+447700900456"
        managerPhone:
          type: string
          description: Manager phone number (used for escalations).
          example: "+447700900789"
        organizationName:
          type: string
          description: Business or restaurant name.
          example: "The Golden Fork"
        address:
          type: string
          description: Full business address.
          example: "12 High Street, London EC2V 6DN"

    ClientResponse:
      type: object
      properties:
        id:
          type: string
          description: MealShift internal client ID.
          example: "cli_abc123"
        clientReference:
          type: string
          example: "rest-001"
        email:
          type: string
          example: "manager@restaurant.co.uk"
        client:
          type: object
          properties:
            name:
              type: string
              example: "James"
            lastname:
              type: string
              example: "Wilson"
            phone:
              type: string
              example: "+447700900456"
        organization:
          type: object
          properties:
            title:
              type: string
              example: "The Golden Fork"
            managerPhone:
              type: string
              example: "+447700900789"
        address:
          type: object
          properties:
            lat:
              type: number
              format: float
              example: 51.5155
            lng:
              type: number
              format: float
              example: -0.0922
            country:
              type: string
              example: "GB"
            city:
              type: string
              example: "London"
            street:
              type: string
              example: "High Street"
            streetNumber:
              type: string
              example: "12"

    QuoteRequest:
      type: object
      required:
        - clientReference
      properties:
        clientReference:
          type: string
          description: Your registered client reference.
          example: "rest-001"
        address:
          type: string
          description: Delivery address as a string. Required if latitude/longitude not provided.
          example: "45 Oxford Street, London W1D 2DZ"
        latitude:
          type: number
          format: float
          description: Delivery latitude. Required if address not provided.
          example: 51.5152
        longitude:
          type: number
          format: float
          description: Delivery longitude. Required if address not provided.
          example: -0.1418
        pickupPoint:
          $ref: '#/components/schemas/PickupPoint'

    QuoteResponse:
      type: object
      properties:
        reference:
          type: string
          description: Quote reference. Pass this when publishing the order to lock in pricing.
          example: "qt_xyz789"
        distanceInMiles:
          type: number
          format: float
          example: 1.99
        distanceInMeters:
          type: integer
          example: 3200
        price:
          $ref: '#/components/schemas/Price'

    PublishOrderRequest:
      type: object
      required:
        - id
        - clientReference
      properties:
        id:
          type: string
          description: Order ID visible to the customer (e.g. receipt number).
          example: "ORD-2024-0042"
        reference:
          type: string
          description: Optional quote reference to lock in quoted price.
          example: "qt_xyz789"
        clientReference:
          type: string
          description: Your registered client reference.
          example: "rest-001"
        customer:
          type: object
          description: End customer details (optional, shown to driver).
          properties:
            name:
              type: string
              example: "Sarah Connor"
            phone:
              type: string
              example: "+447700900321"
        payment:
          type: object
          description: Payment details for the order.
          properties:
            paymentType:
              $ref: '#/components/schemas/PaymentType'
            amount:
              type: integer
              description: Order total in smallest currency unit (e.g. pence).
              example: 2450
        pickupPoint:
          $ref: '#/components/schemas/PickupPoint'
        location:
          allOf:
            - $ref: '#/components/schemas/PickupPoint'
            - description: Dropoff location. Required. Same structure as pickupPoint.
        notes:
          type: string
          description: Delivery instructions for the driver.
          example: "Ring doorbell, leave at door if no answer."

    PublishOrderResponse:
      type: object
      properties:
        id:
          type: string
          example: "ORD-2024-0042"
        reference:
          type: string
          description: MealShift order reference.
          example: "ms_ord_abc123"
        clientReference:
          type: string
          example: "rest-001"
        paymentType:
          $ref: '#/components/schemas/PaymentType'
        price:
          $ref: '#/components/schemas/Price'
        location:
          type: object
          properties:
            distance:
              type: object
              properties:
                meters:
                  type: integer
                  example: 3200
                miles:
                  type: number
                  format: float
                  example: 1.99
            lat:
              type: number
              format: float
              example: 51.5152
            lng:
              type: number
              format: float
              example: -0.1418
            country:
              type: string
              example: "GB"
            city:
              type: string
              example: "London"
            street:
              type: string
              example: "Oxford Street"
            streetNumber:
              type: string
              example: "45"
            flatNumber:
              type: string
              example: ""
            address:
              type: string
              example: "45 Oxford Street, London W1D 2DZ"
        date:
          type: string
          format: date-time
          description: Order creation timestamp (RFC 3339).
          example: "2024-11-15T14:30:00Z"

    OrderDetailsResponse:
      type: object
      properties:
        id:
          type: string
          example: "ORD-2024-0042"
        reference:
          type: string
          example: "ms_ord_abc123"
        clientReference:
          type: string
          example: "rest-001"
        status:
          $ref: '#/components/schemas/OrderStatus'
        paymentType:
          $ref: '#/components/schemas/PaymentType'
        price:
          $ref: '#/components/schemas/Price'
        notes:
          type: string
          example: "Ring doorbell, leave at door if no answer."
        pickupLocation:
          type: object
          properties:
            lat:
              type: number
              format: float
              example: 51.5155
            lng:
              type: number
              format: float
              example: -0.0922
            address:
              type: string
              example: "12 High Street, London EC2V 6DN"
        dropoffLocation:
          $ref: '#/components/schemas/Location'
        distance:
          type: object
          properties:
            original:
              $ref: '#/components/schemas/Distance'
            current:
              allOf:
                - $ref: '#/components/schemas/Distance'
                - nullable: true
                  description: Current remaining distance. Null if driver not yet assigned.
        estimate:
          type: object
          description: Estimated remaining delivery time and distance.
          properties:
            meters:
              type: integer
              example: 1500
            miles:
              type: number
              format: float
              example: 0.93
            estimate:
              type: integer
              description: Seconds until estimated arrival.
              example: 360
        driver:
          $ref: '#/components/schemas/Driver'
        date:
          type: string
          format: date-time
          example: "2024-11-15T14:30:00Z"

    CancelOrderRequest:
      type: object
      required:
        - clientReference
        - orderReference
      properties:
        clientReference:
          type: string
          description: Your registered client reference.
          example: "rest-001"
        orderReference:
          type: string
          description: The MealShift order reference returned when publishing.
          example: "ms_ord_abc123"

    WebhookOrderUpdated:
      type: object
      description: Payload sent to your `updatesWebhook` URL when an order status changes.
      properties:
        clientReference:
          type: string
          example: "rest-001"
        orderReference:
          type: string
          example: "ms_ord_abc123"
        status:
          $ref: '#/components/schemas/OrderStatus'
        driver:
          $ref: '#/components/schemas/Driver'
        date:
          type: string
          format: date-time
          example: "2024-11-15T14:35:00Z"

    WebhookOrderCanceled:
      type: object
      description: Payload sent to your `cancellationsWebhook` URL when an order is canceled.
      properties:
        clientReference:
          type: string
          example: "rest-001"
        orderReference:
          type: string
          example: "ms_ord_abc123"
        date:
          type: string
          format: date-time
          example: "2024-11-15T14:40:00Z"

    Error:
      type: object
      properties:
        error:
          type: string
          example: "invalid_request"
        message:
          type: string
          example: "The clientReference field is required."

paths:
  /api/v2/integration/{partner}/access/generate:
    post:
      operationId: generateCredentials
      tags:
        - Access
      summary: Generate API credentials
      description: |
        Creates a new set of API credentials for your integration. Call this once during initial setup. The response includes your `ms-partner-id` and `secret` - store both securely.

        Optionally provide webhook URLs to start receiving order events immediately.
      parameters:
        - $ref: '#/components/parameters/partnerPath'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookConfig'
            example:
              updatesWebhook: "https://yourapp.com/webhooks/mealshift/updates"
              cancellationsWebhook: "https://yourapp.com/webhooks/mealshift/cancellations"
      responses:
        '200':
          description: Credentials generated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CredentialsResponse'
        '400':
          description: Invalid webhook URL format or unreachable endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Credentials already exist for this partner.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded. Back off and retry with exponential delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error. Contact MealShift support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/integration/access/update-configuration:
    post:
      operationId: updateConfiguration
      tags:
        - Access
      summary: Update webhook configuration
      description: |
        Update your webhook URLs at any time. This replaces any previously configured URLs. Pass `null` to remove a webhook.
      security:
        - partnerId: []
          bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/partnerIdHeader'
        - $ref: '#/components/parameters/partnerTokenHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookConfig'
            example:
              updatesWebhook: "https://pos.example.com/webhooks/mealshift/updates"
              cancellationsWebhook: "https://pos.example.com/webhooks/mealshift/cancellations"
      responses:
        '200':
          description: Configuration updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CredentialsResponse'
        '401':
          description: Missing or invalid authentication. Ensure ms-partner-id header is present and Bearer token is valid and not expired.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded. Back off and retry with exponential delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error. Contact MealShift support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/integration/access/info:
    get:
      operationId: getCredentialsInfo
      tags:
        - Access
      summary: Get credentials info
      description: |
        Retrieve your current credentials and webhook configuration. The `secret` field is partially masked for security.
      security:
        - partnerId: []
      parameters:
        - $ref: '#/components/parameters/partnerIdHeader'
      responses:
        '200':
          description: Current credentials and configuration.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CredentialsResponse'
        '401':
          description: Missing or invalid ms-partner-id header.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded. Back off and retry with exponential delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error. Contact MealShift support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/auth/token:
    post:
      operationId: generateToken
      tags:
        - Authentication
      summary: Generate OAuth token
      description: |
        Exchange your `secret` for a short-lived Bearer token. Include the returned token in the `Authorization` header of all subsequent requests.

        Cache the token and refresh it before the `expiresIn` window elapses. Avoid generating a new token on every API call.
      security:
        - partnerId: []
      parameters:
        - $ref: '#/components/parameters/partnerIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - secret
              properties:
                secret:
                  type: string
                  description: Your API secret.
                  example: "sk_live_abc123def456..."
      responses:
        '200':
          description: Token generated successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                    description: Bearer token for API authorization.
                    example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                  expiresIn:
                    type: integer
                    description: Token lifetime in seconds.
                    example: 3600
        '401':
          description: Invalid secret or partner ID. Ensure the ms-partner-id header matches and the secret is correct.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded. Back off and retry with exponential delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error. Contact MealShift support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/integration/{partner}/clients/register:
    post:
      operationId: registerClient
      tags:
        - Clients
      summary: Register a client
      description: |
        Register a restaurant, venue, or business that will dispatch deliveries through MealShift. Each client gets a unique `clientReference` that you use in all subsequent API calls.

        The address is geocoded automatically. The response includes the resolved coordinates.
      security:
        - partnerId: []
          bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/partnerPath'
        - $ref: '#/components/parameters/partnerIdHeader'
        - $ref: '#/components/parameters/partnerTokenHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClientRegistration'
            example:
              clientReference: "rest-001"
              email: "manager@thecurryhouse.co.uk"
              name: "Raj"
              lastname: "Patel"
              clientPhone: "+447911123456"
              managerPhone: "+447911654321"
              organizationName: "The Curry House"
              address: "42 Brick Lane, London E1 6RF"
      responses:
        '200':
          description: Client registered successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientResponse'
        '400':
          description: Missing required fields (clientReference, name, lastname, clientPhone, organizationName, address) or invalid phone format.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Client with this reference already exists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded. Back off and retry with exponential delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error. Contact MealShift support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/integration/{partner}/request-quote:
    post:
      operationId: requestQuote
      tags:
        - Quotes
      summary: Request delivery quote
      description: |
        Get a price quote for a delivery before publishing the order. Provide either an `address` string or `latitude`/`longitude` coordinates for the dropoff location.

        Optionally include a `pickupPoint` to override the client's registered address.

        The returned `reference` can be passed when publishing the order to lock in the quoted price.
      security:
        - partnerId: []
          bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/partnerPath'
        - $ref: '#/components/parameters/partnerIdHeader'
        - $ref: '#/components/parameters/partnerTokenHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/QuoteRequest'
            examples:
              withAddress:
                summary: Using address string
                value:
                  clientReference: "rest-001"
                  address: "45 Oxford Street, London W1D 2DZ"
              withCoordinates:
                summary: Using coordinates
                value:
                  clientReference: "rest-001"
                  latitude: 51.5152
                  longitude: -0.1418
      responses:
        '200':
          description: Quote calculated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuoteResponse'
        '400':
          description: Missing delivery address or coordinates. Provide either address string or latitude/longitude pair. Client reference must match a registered client.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded. Back off and retry with exponential delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error. Contact MealShift support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/integration/{partner}/publish:
    post:
      operationId: publishOrder
      tags:
        - Orders
      summary: Publish order
      description: |
        Submit an order for delivery. MealShift assigns a driver and begins the dispatch process.

        Required fields:
        - `id` - Your order ID, visible to the customer
        - `clientReference` - The registered client dispatching this order
        - `location` - Dropoff address (required)

        Optional:
        - `reference` - Quote reference to lock in pricing
        - `customer` - End customer name and phone (shown to driver)
        - `payment` - Payment type and order total
        - `pickupPoint` - Override the client's registered pickup address
        - `notes` - Delivery instructions for the driver

        After publishing, track the order via webhooks or by polling the details endpoint.
      security:
        - partnerId: []
          bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/partnerPath'
        - $ref: '#/components/parameters/partnerIdHeader'
        - $ref: '#/components/parameters/partnerTokenHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublishOrderRequest'
            example:
              id: "ORD-2026-0042"
              reference: "pos-ref-0042"
              clientReference: "rest-001"
              customer:
                name: "Sarah Johnson"
                phone: "+447700900123"
              payment:
                paymentType: "PAID"
                amount: 2450
              location:
                latitude: 51.5155
                longitude: -0.0722
                postcode: "E1 6QL"
                country: "GB"
                city: "London"
                street: "Commercial Street"
                streetNumber: "15"
                fullAddress: "15 Commercial Street, London E1 6QL"
              notes: "Ring bell twice, flat 3B"
      responses:
        '200':
          description: Order published successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublishOrderResponse'
        '400':
          description: Missing required fields (id, clientReference, payment, location) or invalid payment type. Payment type must be CASH, CARD, PAID, or ONLINE.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Duplicate order ID. An order with this id already exists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded. Back off and retry with exponential delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error. Contact MealShift support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/integration/{partner}/details:
    get:
      operationId: getOrderDetails
      tags:
        - Orders
      summary: Get order details
      description: |
        Retrieve the full details of an order including current status, driver information, and delivery estimates. Query by either `id` (your order ID) or `reference` (MealShift reference).

        Use this endpoint to poll for order status if webhooks are not configured, or as a fallback when webhook delivery fails.
      security:
        - partnerId: []
          bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/partnerPath'
        - $ref: '#/components/parameters/partnerIdHeader'
        - $ref: '#/components/parameters/partnerTokenHeader'
        - name: id
          in: query
          required: false
          schema:
            type: string
          description: Your order ID. Required if `reference` is not provided.
          example: "ORD-2024-0042"
        - name: reference
          in: query
          required: false
          schema:
            type: string
          description: MealShift order reference. Required if `id` is not provided.
          example: "ms_ord_abc123"
      responses:
        '200':
          description: Order details.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderDetailsResponse'
        '400':
          description: Must provide either id or reference.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Order not found. Check the id or reference parameter matches an existing order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded. Back off and retry with exponential delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error. Contact MealShift support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v2/integration/{partner}/cancel:
    post:
      operationId: cancelOrder
      tags:
        - Orders
      summary: Cancel order
      description: |
        Cancel an active order. Cancellation is only possible before the driver has completed pickup (status is `NEW` or `PICKING_UP`).

        Returns an empty response with HTTP 200 on success. A cancellation webhook event is also dispatched.
      security:
        - partnerId: []
          bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/partnerPath'
        - $ref: '#/components/parameters/partnerIdHeader'
        - $ref: '#/components/parameters/partnerTokenHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CancelOrderRequest'
      responses:
        '200':
          description: Order canceled successfully. Empty response body.
        '400':
          description: Missing clientReference or orderReference, or order cannot be canceled (already picked up or delivered).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Order not found or already completed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded. Back off and retry with exponential delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error. Contact MealShift support if this persists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /webhooks/order-status-updated:
    post:
      operationId: webhookOrderUpdated
      tags:
        - Webhooks
      summary: Order status updated
      description: |
        **This is not a callable endpoint.** This describes the payload MealShift sends to your `updatesWebhook` URL when an order changes status.

        Your endpoint must:
        - Accept POST requests with `Content-Type: application/json`
        - Return a `2xx` status code within 10 seconds
        - Be idempotent (the same event may be delivered more than once)

        Status progression: `NEW` -> `PICKING_UP` -> `IN_PROGRESS` -> `DONE`

        The `driver` field is included once a driver is assigned (from `PICKING_UP` onward).
      x-webhooks: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookOrderUpdated'
            example:
              clientReference: "rest-001"
              orderReference: "ms_ord_abc123"
              status: "PICKING_UP"
              driver:
                name: "Alex M."
                phone: "+447700900123"
                transport: "SCOOTER"
                lat: 51.5080
                lng: -0.1281
              date: "2024-11-15T14:35:00Z"
      responses:
        '200':
          description: Acknowledge receipt. Return any 2xx status.

  /webhooks/order-canceled:
    post:
      operationId: webhookOrderCanceled
      tags:
        - Webhooks
      summary: Order canceled
      description: |
        **This is not a callable endpoint.** This describes the payload MealShift sends to your `cancellationsWebhook` URL when an order is canceled.

        Cancellations can originate from:
        - The partner (via the cancel endpoint)
        - The driver (unable to complete delivery)
        - MealShift system (no driver available within time window)

        Your endpoint must return a `2xx` status code within 10 seconds.
      x-webhooks: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookOrderCanceled'
            example:
              clientReference: "rest-001"
              orderReference: "ms_ord_abc123"
              date: "2024-11-15T14:40:00Z"
      responses:
        '200':
          description: Acknowledge receipt. Return any 2xx status.
