> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leyyow.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Get shipping quotes

> Returns live ShipBubble courier quotes for the given items and delivery address.

All item variants must belong to the authenticated store — cross-store variants are rejected with 400.

Returns 400 with `error_code: SHIPPING_NOT_SETUP` if the store has not connected a ShipBubble shipping account.

Pass the chosen `courier_id` and `amount` as `courier` and `rate` when creating an order with `POST /orders`.



## OpenAPI

````yaml /LeyyowPublicAPI(v2).yaml post /shipping-quotes
openapi: 3.0.3
info:
  title: Leyyow Public API
  version: 1.0.0 (v2)
  description: >-
    The Leyyow Public API lets you build custom storefronts on top of any Leyyow
    merchant store.


    ## Authentication


    Every request must carry a merchant API key as a Bearer token:


    ```

    Authorization: Bearer lyw_<your-secret-key>

    ```


    Keys are scoped to a single store.  All responses contain only data that
    belongs to the store that issued the key.


    ## Rate limits


    | Throttle group | Limit |

    |---|---|

    | Catalogue endpoints (per key) | 1 000 / hour |

    | Catalogue endpoints (per IP) | 200 / hour |

    | Order creation (per key) | 50 / hour |

    | Shipping quotes (per key) | 100 / hour |


    When a limit is exceeded the API returns HTTP 429 with a `Retry-After`
    header indicating how many seconds to wait.


    ## Store scoping


    The `X-Store-Id` header is ignored.  Store identity is derived entirely from
    the API key — there is no way to access a different store's data using the
    same key.
servers:
  - url: /api/public/v1
    description: Leyyow Public API v1
security: []
paths:
  /shipping-quotes:
    post:
      tags:
        - Delivery
      summary: Get shipping quotes
      description: >-
        Returns live ShipBubble courier quotes for the given items and delivery
        address.


        All item variants must belong to the authenticated store — cross-store
        variants are rejected with 400.


        Returns 400 with `error_code: SHIPPING_NOT_SETUP` if the store has not
        connected a ShipBubble shipping account.


        Pass the chosen `courier_id` and `amount` as `courier` and `rate` when
        creating an order with `POST /orders`.
      operationId: shipping_quotes_create
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicShippingQuoteRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/PublicShippingQuoteRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PublicShippingQuoteRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShippingQuotesResponse'
              examples:
                ShippingQuotes:
                  value:
                    quotes:
                      - courier_id: 14
                        courier_name: DHL Express
                        service_type: economy
                        currency: NGN
                        pickup_eta: '2025-01-16T09:00:00Z'
                        delivery_eta: '2025-01-18T17:00:00Z'
                        amount: 4500
                        pickup_fee: 0
                      - courier_id: 22
                        courier_name: GIG Logistics
                        service_type: standard
                        currency: NGN
                        pickup_eta: '2025-01-16T10:00:00Z'
                        delivery_eta: '2025-01-19T17:00:00Z'
                        amount: 2800
                        pickup_fee: 0
                  summary: Shipping quotes
          description: Live courier quotes.
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/_Error'
              examples:
                ValidationError:
                  value:
                    detail:
                      items:
                        - One or more variants do not belong to this store.
                  summary: Validation error
          description: Validation error — see `detail` for field-level errors.
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/_Error'
              examples:
                InvalidKey:
                  value:
                    detail: Invalid or revoked API key.
                    error_code: AUTHENTICATION_FAILED
                  summary: Invalid key
          description: Authentication failed — key missing, invalid, or revoked.
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/_Error'
              examples:
                RateLimited:
                  value:
                    detail: Rate limit exceeded. Please retry in 120 seconds.
                    error_code: ENDPOINT_THROTTLED
                  summary: Rate limited
          description: >-
            Rate limit exceeded.  Retry after the number of seconds indicated in
            the `Retry-After` response header.


            | Throttle group | Limit |

            |---|---|

            | Catalogue endpoints — per API key | 1 000 / hour |

            | Catalogue endpoints — per IP address | 200 / hour |

            | Order creation — per API key | 50 / hour |

            | Shipping quotes — per API key | 100 / hour |
      security:
        - MerchantAPIKey: []
components:
  schemas:
    PublicShippingQuoteRequest:
      type: object
      description: >-
        Request body for POST /api/public/v1/shipping-quotes.


        Mirrors the existing ShippingRatesRequestSerializer but scopes the
        variant

        queryset to the authenticated store's variants.
      properties:
        delivery_address:
          type: string
        customer_name:
          type: string
        customer_email:
          type: string
          format: email
        customer_phone:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/ShippingRatesVariant'
      required:
        - customer_email
        - customer_name
        - customer_phone
        - delivery_address
        - items
    ShippingQuotesResponse:
      type: object
      properties:
        quotes:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: List of courier quotes from ShipBubble.
      required:
        - quotes
    _Error:
      type: object
      properties:
        detail:
          type: string
        error_code:
          type: string
      required:
        - detail
    ShippingRatesVariant:
      type: object
      description: Serializer for shipping rates variant
      properties:
        variant:
          type: string
          format: uuid
          nullable: true
        quantity:
          type: integer
          maximum: 2147483647
          minimum: 0
  securitySchemes:
    MerchantAPIKey:
      type: http
      scheme: bearer
      bearerFormat: lyw_<secret>
      description: >-
        Authenticate using a Leyyow merchant API key.


        Include the key in every request as a Bearer token:


        ```

        Authorization: Bearer lyw_<your-secret-key>

        ```


        Keys are scoped to a single store. All endpoints return data belonging
        to the store that issued the key — there is no `X-Store-Id` override.


        Keys are created in the Leyyow merchant dashboard under **Settings →
        Developer → API Keys**.

````