openapi: 3.1.0
info:
  title: Shioja API
  version: 2.0.0
  description: |
    Shioja API v2 provides product discovery, delivery-fee previews,
    server-priced idempotent order creation, and private order and wallet reads.
    API v1 remains available for backward compatibility.
  contact:
    email: support@shioja.com
servers:
  - url: https://api.shioja.com
    description: Production and test traffic; the API key selects the mode.
security:
  - ApiKey: []
tags:
  - name: Platform
  - name: Products
  - name: Delivery
  - name: Orders
  - name: Wallet
paths:
  /v2/status:
    get:
      tags: [Platform]
      operationId: getApiStatus
      summary: Check API status
      security: []
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /v2/getShortProducts:
    get:
      tags: [Products]
      operationId: listProducts
      summary: List in-stock products
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/ProductPageSize"
        - $ref: "#/components/parameters/ProductSortBy"
        - $ref: "#/components/parameters/SortAscending"
        - name: lowerPriceRange
          in: query
          schema: {type: number, minimum: 0}
        - name: upperPriceRange
          in: query
          schema: {type: number, minimum: 0}
        - name: brandFilter
          in: query
          schema: {type: string}
          description: Comma-separated exact brand values.
        - name: categoryFilter
          in: query
          schema: {type: string}
        - name: familyFilter
          in: query
          schema: {type: string}
      responses:
        "200":
          $ref: "#/components/responses/ProductList"
        "400":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v2/getLongProduct:
    get:
      tags: [Products]
      operationId: getProduct
      summary: Get canonical product details
      parameters:
        - name: productId
          in: query
          required: true
          schema: {type: string}
      responses:
        "200":
          $ref: "#/components/responses/Product"
        "404":
          $ref: "#/components/responses/Error"
  /v2/getManyProducts:
    get:
      tags: [Products]
      operationId: getManyProducts
      summary: Get multiple products
      parameters:
        - name: productIds
          in: query
          required: true
          schema: {type: string}
          description: Comma-separated product IDs.
        - $ref: "#/components/parameters/ProductSortBy"
        - $ref: "#/components/parameters/SortAscending"
      responses:
        "200":
          $ref: "#/components/responses/ProductList"
  /v2/searchProducts:
    get:
      tags: [Products]
      operationId: searchProducts
      summary: Search and hydrate products
      parameters:
        - name: query
          in: query
          schema: {type: string}
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/ProductPageSize"
        - name: brandFilter
          in: query
          schema: {type: string}
        - name: categoryFilter
          in: query
          schema: {type: string}
      responses:
        "200":
          $ref: "#/components/responses/ProductList"
  /v2/getBrands:
    get:
      tags: [Products]
      operationId: getBrands
      summary: Get product brands
      responses:
        "200":
          $ref: "#/components/responses/StringList"
  /v2/getCategories:
    get:
      tags: [Products]
      operationId: getCategories
      summary: Get product categories
      responses:
        "200":
          $ref: "#/components/responses/StringList"
  /v2/delivery/states:
    get:
      tags: [Delivery]
      operationId: getDeliveryStates
      summary: List supported states
      responses:
        "200":
          $ref: "#/components/responses/StringList"
  /v2/delivery/lgas:
    get:
      tags: [Delivery]
      operationId: getDeliveryLgas
      summary: List LGAs for a state
      parameters:
        - name: state
          in: query
          required: true
          schema: {type: string}
      responses:
        "200":
          $ref: "#/components/responses/StringList"
  /v2/delivery/towns:
    get:
      tags: [Delivery]
      operationId: getDeliveryTowns
      summary: List towns for a state and LGA
      parameters:
        - name: state
          in: query
          required: true
          schema: {type: string}
        - name: lga
          in: query
          required: true
          schema: {type: string}
      responses:
        "200":
          $ref: "#/components/responses/StringList"
  /v2/delivery/fee:
    get:
      tags: [Delivery]
      operationId: getDeliveryFee
      summary: Preview the delivery fee for a product and location
      parameters:
        - name: productId
          in: query
          required: true
          schema: {type: string}
        - name: state
          in: query
          required: true
          schema: {type: string}
        - name: lga
          in: query
          required: true
          schema: {type: string}
        - name: town
          in: query
          required: true
          schema: {type: string}
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "400":
          $ref: "#/components/responses/Error"
  /v2/orderProduct:
    post:
      tags: [Orders]
      operationId: createOrder
      summary: Create and charge an order idempotently
      description: Requires a test_sk or live_sk secret key.
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema: {type: string, minLength: 8, maxLength: 200}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateOrder"
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "402":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
  /v2/orders:
    get:
      tags: [Orders]
      operationId: listOrders
      summary: List orders belonging to the key's app and mode
      description: Requires a secret key.
      parameters:
        - $ref: "#/components/parameters/Page"
        - name: noOfOrdersPerPage
          in: query
          schema: {type: integer, minimum: 1, maximum: 100, default: 20}
        - name: sortBy
          in: query
          schema:
            type: string
            enum: [dateStarted, dateOrdered, deliveryStatus, totalAmount]
            default: dateStarted
        - $ref: "#/components/parameters/SortAscending"
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /v2/orders/{orderId}:
    get:
      tags: [Orders]
      operationId: getOrder
      summary: Get one order
      description: Requires a secret key from the order's app and mode.
      parameters:
        - $ref: "#/components/parameters/OrderId"
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "404":
          $ref: "#/components/responses/Error"
  /v2/orders/{orderId}/status:
    get:
      tags: [Orders]
      operationId: getOrderStatus
      summary: Get delivery status for one order
      description: Requires a secret key from the order's app and mode.
      parameters:
        - $ref: "#/components/parameters/OrderId"
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "404":
          $ref: "#/components/responses/Error"
  /v2/wallet/balance:
    get:
      tags: [Wallet]
      operationId: getWalletBalance
      summary: Get the key environment's wallet balance
      description: Requires a secret key.
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /v2/wallet/entries:
    get:
      tags: [Wallet]
      operationId: getWalletEntries
      summary: Get recent wallet entries
      description: Requires a secret key.
      parameters:
        - name: limit
          in: query
          schema: {type: integer, minimum: 1, maximum: 200, default: 50}
      responses:
        "200":
          $ref: "#/components/responses/Success"
  /v2/wallet/funding-account:
    get:
      tags: [Wallet]
      operationId: getFundingAccount
      summary: Get an existing dedicated live funding account
      description: Requires a secret key. Account creation is dashboard-only.
      responses:
        "200":
          $ref: "#/components/responses/Success"
        "404":
          $ref: "#/components/responses/Error"
components:
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: apiKey
      description: Public keys allow reads. Secret keys are required for private and write operations.
  parameters:
    Page:
      name: page
      in: query
      schema: {type: integer, minimum: 1, default: 1}
    ProductPageSize:
      name: noOfProductsPerPage
      in: query
      schema: {type: integer, minimum: 1, maximum: 100, default: 10}
    ProductSortBy:
      name: sortBy
      in: query
      schema:
        type: string
        enum: [price, brand, category, family, name, onSale]
        default: price
    SortAscending:
      name: sortAscending
      in: query
      schema: {type: boolean, default: true}
    OrderId:
      name: orderId
      in: path
      required: true
      schema: {type: string, minLength: 5}
  schemas:
    DeliveryAddress:
      type: object
      required: [version, address, state, lga, town]
      properties:
        version: {type: string, const: "2"}
        address: {type: string}
        state: {type: string}
        lga: {type: string}
        town: {type: string}
        pickUp:
          type: boolean
          enum: [false]
          default: false
          description: Shioja orders are delivery-only; true is rejected.
    CreateOrder:
      type: object
      required: [productId, deliveryAddress, nameOfCustomer, phoneNumber]
      properties:
        productId: {type: string}
        deliveryAddress:
          $ref: "#/components/schemas/DeliveryAddress"
        nameOfCustomer: {type: string}
        phoneNumber: {type: string}
        buyerEmail: {type: string, format: email}
        idempotencyKey:
          type: string
          description: Body fallback when custom headers cannot be sent.
    Product:
      type: object
      additionalProperties: true
      properties:
        id: {type: string}
        name: {type: string}
        price: {type: number}
        brand: {type: string}
        category: {type: string}
        inStock: {type: boolean}
    Envelope:
      type: object
      properties:
        status: {type: string}
        message: {type: string}
        data: {}
    Error:
      type: object
      properties:
        status: {type: string, examples: [error]}
        code: {type: string}
        message: {type: string}
        data: {}
  responses:
    Success:
      description: Successful response
      headers:
        RateLimit-Limit:
          schema: {type: integer}
        RateLimit-Remaining:
          schema: {type: integer}
        RateLimit-Reset:
          schema: {type: integer}
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Envelope"
    Product:
      description: Product response
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Envelope"
              - type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Product"
    ProductList:
      description: Product list response
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Envelope"
              - type: object
                properties:
                  page: {type: integer}
                  hasNextpage: {type: boolean}
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Product"
    StringList:
      description: Sorted string-list response
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Envelope"
              - type: object
                properties:
                  data:
                    type: array
                    items: {type: string}
    Error:
      description: Request failed
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    RateLimited:
      description: Too many requests
      headers:
        Retry-After:
          schema: {type: integer}
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
