openapi: 3.1.0
info:
  title: Relay Public and Business API
  version: 1.0.0
  description: Contract for customer, operations, dispatch, driver, payment and proof-of-delivery
    workflows.
servers:
- url: https://api.relay.techminemw.com/api/v1
  description: Relay production API
security:
- bearerAuth: []
tags:
- name: Public
- name: Authentication
- name: Shipments
- name: Dispatch
- name: Fleet
- name: Customers
- name: Finance
- name: Support
- name: Administration
- name: Subscriptions
  description: A tenant's own Relay subscription and usage.
- name: RelayAdmin
  description: Relay platform administration across all tenants (platform-admin role).
paths:
  /health:
    get:
      security: []
      tags:
      - Public
      operationId: health
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Health'
  /tracking/{trackingNumber}:
    get:
      security: []
      tags:
      - Public
      operationId: trackShipment
      parameters:
      - in: path
        name: trackingNumber
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Public shipment timeline
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicTrackingResponse'
        '404':
          $ref: '#/components/responses/NotFound'
  /auth/login:
    post:
      security: []
      tags:
      - Authentication
      operationId: login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - email
              - password
              - device_name
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  format: password
                device_name:
                  type: string
      responses:
        '200':
          description: Token and user profile
        '422':
          $ref: '#/components/responses/ValidationError'
  /auth/me:
    get:
      tags:
      - Authentication
      operationId: currentUser
      responses:
        '200':
          description: Authenticated user
  /auth/logout:
    post:
      tags:
      - Authentication
      operationId: logout
      responses:
        '204':
          description: Token revoked
  /dashboard:
    get:
      tags:
      - Administration
      operationId: operationsDashboard
      responses:
        '200':
          description: Branch-scoped operational summary
  /subscription:
    get:
      tags:
      - Billing
      operationId: showSubscription
      summary: Current tenant's Relay subscription and entitlement usage
      responses:
        '200':
          description: Subscription plan, status, price, period and per-resource usage
        '404':
          description: The tenant has no subscription or no tenant context
  /branches:
    get:
      tags:
      - Administration
      operationId: listBranches
      responses:
        '200':
          description: Active branches and depots
  /shipments:
    get:
      tags:
      - Shipments
      operationId: listShipments
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/PerPage'
      - in: query
        name: filter[status]
        schema:
          $ref: '#/components/schemas/ShipmentStatus'
      responses:
        '200':
          description: Paginated shipment registry
    post:
      tags:
      - Shipments
      operationId: createShipment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateShipment'
      responses:
        '201':
          description: Shipment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
        '422':
          $ref: '#/components/responses/ValidationError'
  /shipments/{shipmentId}:
    parameters:
    - $ref: '#/components/parameters/ShipmentId'
    get:
      tags:
      - Shipments
      operationId: getShipment
      responses:
        '200':
          description: Shipment aggregate
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
      - Shipments
      operationId: updateShipment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - version
              properties:
                version:
                  type: integer
                  minimum: 1
                recipient_name:
                  type: string
                recipient_phone:
                  type: string
                scheduled_pickup_at:
                  type: string
                  format: date-time
                  nullable: true
                special_instructions:
                  type: string
                  nullable: true
      responses:
        '200':
          description: Shipment updated
        '409':
          $ref: '#/components/responses/Conflict'
  /shipments/{shipmentId}/transition:
    post:
      tags:
      - Shipments
      operationId: transitionShipment
      parameters:
      - $ref: '#/components/parameters/ShipmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - status
              - version
              properties:
                status:
                  $ref: '#/components/schemas/ShipmentStatus'
                version:
                  type: integer
                  minimum: 1
                title:
                  type: string
                description:
                  type: string
                  nullable: true
                latitude:
                  type: number
                  nullable: true
                longitude:
                  type: number
                  nullable: true
                occurred_at:
                  type: string
                  format: date-time
      responses:
        '200':
          description: Transition accepted
        '409':
          $ref: '#/components/responses/Conflict'
  /shipments/{shipmentId}/proofs:
    post:
      tags:
      - Shipments
      operationId: captureDeliveryProof
      parameters:
      - $ref: '#/components/parameters/ShipmentId'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/DeliveryProofInput'
      responses:
        '201':
          description: Proof stored and shipment delivered
  /shipments/{shipmentId}/exceptions:
    post:
      tags:
      - Shipments
      operationId: createShipmentException
      parameters:
      - $ref: '#/components/parameters/ShipmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - type
              - severity
              - reason
              properties:
                type:
                  type: string
                severity:
                  type: string
                  enum:
                  - low
                  - medium
                  - high
                  - critical
                reason:
                  type: string
                notes:
                  type: string
                  nullable: true
      responses:
        '201':
          description: Exception recorded
  /dispatch/board:
    get:
      tags:
      - Dispatch
      operationId: dispatchBoard
      responses:
        '200':
          description: Unassigned shipments
          available drivers: null
          vehicles and active manifests: null
  /dispatch/shipments/{shipmentId}/assign:
    post:
      tags:
      - Dispatch
      operationId: assignShipment
      parameters:
      - $ref: '#/components/parameters/ShipmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - driver_id
              - vehicle_id
              - scheduled_at
              properties:
                driver_id:
                  type: string
                  format: uuid
                vehicle_id:
                  type: string
                  format: uuid
                scheduled_at:
                  type: string
                  format: date-time
                notes:
                  type: string
                  nullable: true
      responses:
        '201':
          description: Manifest and assignment created
        '409':
          $ref: '#/components/responses/Conflict'
  /manifests:
    get:
      tags:
      - Dispatch
      operationId: listManifests
      responses:
        '200':
          description: Paginated manifests
  /manifests/{manifestId}:
    get:
      tags:
      - Dispatch
      operationId: getManifest
      parameters:
      - in: path
        name: manifestId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Manifest detail
  /manifests/{manifestId}/start:
    post:
      tags:
      - Dispatch
      operationId: startManifest
      parameters:
      - in: path
        name: manifestId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Manifest started
  /manifests/{manifestId}/complete:
    post:
      tags:
      - Dispatch
      operationId: completeManifest
      parameters:
      - in: path
        name: manifestId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Manifest completed
  /fleet/drivers:
    get:
      tags:
      - Fleet
      operationId: listDrivers
      responses:
        '200':
          description: Driver readiness and assignment data
  /fleet/vehicles:
    get:
      tags:
      - Fleet
      operationId: listVehicles
      responses:
        '200':
          description: Vehicle readiness and compliance data
  /driver/location:
    post:
      tags:
      - Fleet
      operationId: reportDriverLocation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - latitude
              - longitude
              - captured_at
              - device_id
              properties:
                latitude:
                  type: number
                longitude:
                  type: number
                captured_at:
                  type: string
                  format: date-time
                device_id:
                  type: string
      responses:
        '200':
          description: Location accepted
        '409':
          $ref: '#/components/responses/Conflict'
  /customers:
    get:
      tags:
      - Customers
      operationId: listCustomers
      responses:
        '200':
          description: Paginated customer accounts
    post:
      tags:
      - Customers
      operationId: createCustomer
      responses:
        '201':
          description: Customer created
  /customers/{customerId}:
    get:
      tags:
      - Customers
      operationId: getCustomer
      parameters:
      - in: path
        name: customerId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Customer detail
    put:
      tags:
      - Customers
      operationId: updateCustomer
      parameters:
      - in: path
        name: customerId
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Customer updated
  /shipments/{shipmentId}/payment-intents:
    post:
      tags:
      - Finance
      operationId: createPaymentIntent
      parameters:
      - $ref: '#/components/parameters/ShipmentId'
      responses:
        '201':
          description: New provider intent created
        '200':
          description: Existing idempotent provider intent returned
        '409':
          $ref: '#/components/responses/Conflict'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - method
              - idempotency_key
              properties:
                method:
                  $ref: '#/components/schemas/PaymentMethod'
                idempotency_key:
                  type: string
                  format: uuid
  /billing/webhook:
    post:
      security: []
      tags:
      - Billing
      operationId: saasBillingWebhook
      summary: PayChangu SaaS billing webhook (HMAC-signed, settled server-side)
      responses:
        '200':
          description: Event accepted; settlement is idempotent and replay-safe
        '400':
          description: Invalid signature or payload
      parameters:
      - in: header
        name: X-Paychangu-Signature
        required: true
        schema:
          type: string
  /payments/webhook:
    post:
      security: []
      tags:
      - Finance
      operationId: paymentWebhook
      responses:
        '200':
          description: Provider event accepted idempotently
        '401':
          description: Invalid signature
      parameters:
      - in: header
        name: X-Siku-Signature
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - reference
              - status
              properties:
                reference:
                  type: string
                status:
                  $ref: '#/components/schemas/PaymentStatus'
  /reports/overview:
    get:
      tags:
      - Administration
      operationId: reportOverview
      parameters:
      - in: query
        name: from
        schema:
          type: string
          format: date
      - in: query
        name: to
        schema:
          type: string
          format: date
      responses:
        '200':
          description: KPI snapshot
  /support/tickets:
    get:
      tags:
      - Support
      operationId: listSupportTickets
      responses:
        '200':
          description: Support queue
    post:
      tags:
      - Support
      operationId: createSupportTicket
      responses:
        '201':
          description: Ticket created
  /team/roles:
    get:
      tags:
      - Administration
      operationId: listRoles
      responses:
        '200':
          description: Roles with permissions and user counts
  /team/users:
    get:
      tags:
      - Administration
      operationId: listUsers
      responses:
        '200':
          description: Paginated platform users
    post:
      tags:
      - Administration
      operationId: createUser
      responses:
        '201':
          description: User created with assigned role
  /team/permissions:
    get:
      tags:
      - Administration
      operationId: listPermissions
      responses:
        '200':
          description: Permission catalogue
  /sync/batch:
    post:
      tags:
      - Shipments
      operationId: syncOfflineCommands
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - commands
              properties:
                commands:
                  type: array
                  maxItems: 100
                  items:
                    $ref: '#/components/schemas/OfflineCommand'
      responses:
        '200':
          description: Per-command acceptance or rejection results
  /payments:
    get:
      tags:
      - Finance
      operationId: listPayments
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/PerPage'
      - in: query
        name: status
        schema:
          $ref: '#/components/schemas/PaymentStatus'
      responses:
        '200':
          description: Customer- or branch-scoped payment registry
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Payment'
  /exceptions/{exceptionId}/resolve:
    post:
      tags:
      - Shipments
      operationId: resolveShipmentException
      parameters:
      - in: path
        name: exceptionId
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - resolution
              properties:
                resolution:
                  type: string
                  maxLength: 2000
      responses:
        '200':
          description: Exception resolved
  /support/tickets/{ticketId}/resolve:
    post:
      tags:
      - Support
      operationId: resolveSupportTicket
      parameters:
      - in: path
        name: ticketId
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - resolution
              properties:
                resolution:
                  type: string
                  maxLength: 3000
      responses:
        '200':
          description: Ticket resolved
  /app/version:
    get:
      security: []
      tags:
      - Public
      operationId: getAppVersion
      description: Mobile app update management — latest and minimum-supported build per platform.
      parameters:
      - in: query
        name: platform
        required: false
        schema:
          type: string
          enum:
          - android
          - ios
      - in: query
        name: version
        required: false
        schema:
          type: string
      responses:
        '200':
          description: Version policy for the requested platform
  /subscription:
    get:
      tags:
      - Subscriptions
      operationId: getSubscription
      description: The authenticated tenant's own subscription, plan and usage.
      responses:
        '200':
          description: Current subscription with usage against plan limits
  /admin/plans:
    get:
      tags:
      - RelayAdmin
      operationId: listPlans
      description: Relay plan catalogue (platform-admin).
      responses:
        '200':
          description: Plans
        '403':
          description: Not a platform admin
  /admin/tenants:
    get:
      tags:
      - RelayAdmin
      operationId: listTenants
      responses:
        '200':
          description: Tenants
        '403':
          description: Not a platform admin
    post:
      tags:
      - RelayAdmin
      operationId: createTenant
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              - slug
              - plan_code
              - admin
              properties:
                name:
                  type: string
                slug:
                  type: string
                legal_name:
                  type: string
                domain:
                  type: string
                country:
                  type: string
                currency:
                  type: string
                plan_code:
                  type: string
                admin:
                  type: object
                  required:
                  - name
                  - email
                  - password
                  properties:
                    name:
                      type: string
                    email:
                      type: string
                      format: email
                    password:
                      type: string
                      format: password
      responses:
        '201':
          description: Tenant provisioned with an admin user and subscription
        '403':
          description: Not a platform admin
  /admin/tenants/{tenant}:
    get:
      tags:
      - RelayAdmin
      operationId: getTenant
      parameters:
      - in: path
        name: tenant
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Tenant
    patch:
      tags:
      - RelayAdmin
      operationId: updateTenant
      parameters:
      - in: path
        name: tenant
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                legal_name:
                  type: string
                domain:
                  type: string
                country:
                  type: string
                currency:
                  type: string
      responses:
        '200':
          description: Tenant updated
  /admin/tenants/{tenant}/suspend:
    post:
      tags:
      - RelayAdmin
      operationId: suspendTenant
      parameters:
      - in: path
        name: tenant
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Tenant suspended
        '422':
          description: The founding tenant cannot be suspended
  /admin/tenants/{tenant}/activate:
    post:
      tags:
      - RelayAdmin
      operationId: activateTenant
      parameters:
      - in: path
        name: tenant
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Tenant activated
  /admin/tenants/{tenant}/subscription:
    post:
      tags:
      - RelayAdmin
      operationId: subscribeTenant
      parameters:
      - in: path
        name: tenant
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - plan_code
              properties:
                plan_code:
                  type: string
      responses:
        '201':
          description: Subscription changed; a platform invoice may be raised
  /admin/subscriptions:
    get:
      tags:
      - RelayAdmin
      operationId: listSubscriptions
      responses:
        '200':
          description: All tenant subscriptions
        '403':
          description: Not a platform admin
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Sanctum
  parameters:
    ShipmentId:
      in: path
      name: shipmentId
      required: true
      schema:
        type: string
        format: uuid
    Page:
      in: query
      name: page
      schema:
        type: integer
        minimum: 1
    PerPage:
      in: query
      name: per_page
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
  schemas:
    Health:
      type: object
      required:
      - status
      - service
      - version
      - time
      properties:
        status:
          type: string
          const: ok
        service:
          type: string
        version:
          type: string
        time:
          type: string
          format: date-time
    ShipmentStatus:
      type: string
      enum:
      - draft
      - quotation_requested
      - awaiting_approval
      - awaiting_payment
      - confirmed
      - pickup_scheduled
      - driver_assigned
      - en_route_to_pickup
      - arrived_at_pickup
      - picked_up
      - at_origin_branch
      - sorted
      - loaded
      - in_transit
      - at_destination_branch
      - out_for_delivery
      - delivery_attempted
      - delivered
      - delivery_failed
      - held
      - return_requested
      - returning_to_sender
      - returned
      - cancelled
      - lost
      - damaged
      - under_investigation
    Shipment:
      type: object
      required:
      - id
      - tracking_number
      - status
      - status_label
      - service_type
      - sender
      - recipient
      - pricing
      - version
      properties:
        id:
          type: string
          format: uuid
        tracking_number:
          type: string
        status:
          $ref: '#/components/schemas/ShipmentStatus'
        status_label:
          type: string
        service_type:
          type: string
        sender:
          $ref: '#/components/schemas/ContactSummary'
        recipient:
          $ref: '#/components/schemas/ContactSummary'
        origin:
          $ref: '#/components/schemas/BranchSummary'
        destination:
          $ref: '#/components/schemas/BranchSummary'
        packages:
          type: array
          items:
            $ref: '#/components/schemas/Package'
        timeline:
          type: array
          items:
            $ref: '#/components/schemas/TrackingEvent'
        pricing:
          $ref: '#/components/schemas/PricingSummary'
        scheduled_pickup_at:
          type:
          - string
          - 'null'
          format: date-time
        estimated_delivery_at:
          type:
          - string
          - 'null'
          format: date-time
        delivered_at:
          type:
          - string
          - 'null'
          format: date-time
        version:
          type: integer
          minimum: 1
        created_at:
          type:
          - string
          - 'null'
          format: date-time
    Package:
      type: object
      required:
      - label
      - quantity
      - weight_kg
      properties:
        label:
          type: string
        description:
          type: string
          nullable: true
        quantity:
          type: integer
          minimum: 1
        weight_kg:
          type: number
          exclusiveMinimum: 0
        length_cm:
          type: number
          nullable: true
        width_cm:
          type: number
          nullable: true
        height_cm:
          type: number
          nullable: true
        declared_value:
          type: number
          nullable: true
        is_fragile:
          type: boolean
    CreateShipment:
      type: object
      required:
      - origin_branch_id
      - destination_branch_id
      - service_type
      - sender_name
      - sender_phone
      - recipient_name
      - recipient_phone
      - packages
      properties:
        customer_id:
          type: string
          format: uuid
          description: Required for staff-created bookings; customer tokens are always
            forced to their own customer account.
        origin_branch_id:
          type: string
          format: uuid
        destination_branch_id:
          type: string
          format: uuid
        pickup_address_id:
          type: string
          format: uuid
          nullable: true
        delivery_address_id:
          type: string
          format: uuid
          nullable: true
        service_type:
          type: string
        sender_name:
          type: string
        sender_phone:
          type: string
        recipient_name:
          type: string
        recipient_phone:
          type: string
        packages:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/Package'
        scheduled_pickup_at:
          type:
          - string
          - 'null'
          format: date-time
        special_instructions:
          type:
          - string
          - 'null'
          maxLength: 1000
    DeliveryProofInput:
      type: object
      required:
      - recipient_name
      - otp_verified
      - latitude
      - longitude
      - captured_at
      - device_id
      properties:
        recipient_name:
          type: string
        recipient_phone:
          type: string
          nullable: true
        recipient_relationship:
          type: string
          nullable: true
        otp_verified:
          type: boolean
        latitude:
          type: number
        longitude:
          type: number
        captured_at:
          type: string
          format: date-time
        device_id:
          type: string
        signature:
          type: string
          format: binary
          nullable: true
        photo:
          type: string
          format: binary
          nullable: true
    OfflineCommand:
      type: object
      required:
      - id
      - type
      - payload
      - created_at
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
          - shipment_transition
          - delivery_proof
          - location_ping
        payload:
          type: object
          additionalProperties: true
        created_at:
          type: string
          format: date-time
    Error:
      type: object
      properties:
        message:
          type: string
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
    ContactSummary:
      type: object
      required:
      - name
      - phone
      properties:
        name:
          type: string
        phone:
          type: string
    BranchSummary:
      type:
      - object
      - 'null'
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        city:
          type: string
    PricingSummary:
      type: object
      required:
      - subtotal
      - tax
      - total
      - currency
      properties:
        subtotal:
          type: string
        tax:
          type: string
        total:
          type: string
        currency:
          type: string
          minLength: 3
          maxLength: 3
    TrackingEvent:
      type: object
      required:
      - id
      - status
      - title
      - occurred_at
      properties:
        id:
          type: string
          format: uuid
        status:
          $ref: '#/components/schemas/ShipmentStatus'
        title:
          type: string
        description:
          type:
          - string
          - 'null'
        branch:
          $ref: '#/components/schemas/BranchSummary'
        location:
          type:
          - object
          - 'null'
          properties:
            latitude:
              type: number
            longitude:
              type: number
            precision:
              type: string
              enum:
              - exact
              - approximate
        occurred_at:
          type: string
          format: date-time
    PublicTrackingResponse:
      type: object
      required:
      - data
      properties:
        data:
          type: object
          required:
          - tracking_number
          - status
          - status_label
          - service_type
          - recipient
          - timeline
          properties:
            tracking_number:
              type: string
            status:
              $ref: '#/components/schemas/ShipmentStatus'
            status_label:
              type: string
            service_type:
              type: string
            recipient:
              type: object
              properties:
                name:
                  type:
                  - string
                  - 'null'
                  description: Masked recipient name
            origin:
              $ref: '#/components/schemas/BranchSummary'
            destination:
              $ref: '#/components/schemas/BranchSummary'
            timeline:
              type: array
              items:
                $ref: '#/components/schemas/TrackingEvent'
            estimated_delivery_at:
              type:
              - string
              - 'null'
              format: date-time
            delivered_at:
              type:
              - string
              - 'null'
              format: date-time
    PaymentStatus:
      type: string
      enum:
      - pending
      - processing
      - successful
      - failed
      - reversed
      - refunded
      - partially_refunded
    PaymentMethod:
      type: string
      enum:
      - mobile_money
      - card
      - bank_transfer
      - cash
      - credit_account
      - cash_on_delivery
    Payment:
      type: object
      required:
      - id
      - shipment_id
      - method
      - status
      - currency
      - amount
      properties:
        id:
          type: string
          format: uuid
        shipment_id:
          type: string
          format: uuid
        provider:
          type: string
        provider_reference:
          type:
          - string
          - 'null'
        method:
          $ref: '#/components/schemas/PaymentMethod'
        status:
          $ref: '#/components/schemas/PaymentStatus'
        currency:
          type: string
        amount:
          type: string
        paid_at:
          type:
          - string
          - 'null'
          format: date-time
  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ValidationError:
      description: Request validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: State, version or resource availability conflict
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
