openapi: 3.0.3
info:
  title: Everest Partner API
  version: "1.0.0"
  description: >
    Stable partner-facing API for business onboarding, delivery quoting,
    delivery creation, sandbox simulation, webhooks, billing, and portal
    operations.
servers:
  - url: https://api.everestmobility.co/api/partners/v1
    description: Production
  - url: https://staging-api.everestmobility.co/api/partners/v1
    description: Staging
  - url: http://localhost:8000/api/partners/v1
    description: Local development
tags:
  - name: Applications
  - name: Deliveries
  - name: Sandbox
  - name: Webhooks
  - name: Billing
  - name: Portal
  - name: Everest Review
security:
  - PartnerApiKey: []
paths:
  /applications/:
    post:
      operationId: postApplications
      tags: [Applications]
      summary: Submit a partner application.
      description: Everest emails the application ID and a status-page link to the contact email. The create response still returns the application ID if notification delivery fails.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerApplicationCreate"
      responses:
        "201":
          description: Application submitted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerApplication"
        "429":
          $ref: "#/components/responses/PartnerError"
  /applications/{application_id}/status/:
    get:
      operationId: getApplicationsByApplicationIdStatus
      tags: [Applications]
      summary: Check partner application review status.
      description: Public applicant status lookup. Requires the application ID and matching contact email; activation tokens and raw keys are never returned.
      security: []
      parameters:
        - name: application_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: contact_email
          in: query
          required: true
          schema:
            type: string
            format: email
      responses:
        "200":
          description: Application status.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerApplicationStatus"
        "404":
          $ref: "#/components/responses/PartnerError"
  /admin/applications/:
    get:
      operationId: getAdminApplications
      tags: [Everest Review]
      summary: List partner applications for Everest review.
      security:
        - UserJwt: []
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [PENDING, APPROVED, REJECTED]
      responses:
        "200":
          description: Application list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/PartnerApplication"
  /admin/applications/{application_id}/approve/:
    post:
      operationId: postAdminApplicationsByApplicationIdApprove
      tags: [Everest Review]
      summary: Approve a partner application and optionally issue a sandbox key.
      security:
        - UserJwt: []
      parameters:
        - name: application_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerApplicationApprove"
      responses:
        "200":
          description: Approved application with one-time sandbox credentials when issued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerApplicationApproval"
  /admin/applications/{application_id}/reject/:
    post:
      operationId: postAdminApplicationsByApplicationIdReject
      tags: [Everest Review]
      summary: Reject a partner application.
      security:
        - UserJwt: []
      parameters:
        - name: application_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerApplicationReject"
      responses:
        "200":
          description: Rejected application.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerApplication"
  /admin/accounts/:
    get:
      operationId: getAdminAccounts
      tags: [Everest Review]
      summary: List partner accounts for Everest operators.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Partner account list.
    post:
      operationId: postAdminAccounts
      tags: [Everest Review]
      summary: Create a partner account for an existing approved business.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "201":
          description: Partner account created.
  /admin/accounts/{partner_id}/:
    get:
      operationId: getAdminAccountsByPartnerId
      tags: [Everest Review]
      summary: Read a partner account.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/PartnerId"
      responses:
        "200":
          description: Partner account.
    patch:
      operationId: patchAdminAccountsByPartnerId
      tags: [Everest Review]
      summary: Update partner account policy and contact settings.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/PartnerId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Updated partner account.
  /admin/accounts/{partner_id}/credentials/:
    get:
      operationId: getAdminAccountsByPartnerIdCredentials
      tags: [Everest Review]
      summary: List credentials for a partner account.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/PartnerId"
      responses:
        "200":
          description: Credential list without raw keys.
    post:
      operationId: postAdminAccountsByPartnerIdCredentials
      tags: [Everest Review]
      summary: Issue a credential for a partner account.
      description: Returns `api_key` once.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/PartnerId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CredentialIssue"
      responses:
        "201":
          description: Credential issued with a one-time raw key.
  /admin/accounts/{partner_id}/credentials/{credential_id}/revoke/:
    post:
      operationId: postAdminAccountsByPartnerIdCredentialsByCredentialIdRevoke
      tags: [Everest Review]
      summary: Revoke a partner credential.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/PartnerId"
        - $ref: "#/components/parameters/CredentialId"
      responses:
        "200":
          description: Revoked credential.
  /admin/accounts/{partner_id}/audit-logs/:
    get:
      operationId: getAdminAccountsByPartnerIdAuditLogs
      tags: [Everest Review]
      summary: List API audit events for a partner account.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/PartnerId"
      responses:
        "200":
          description: Partner API audit events.
  /portal/auth/activate/:
    post:
      operationId: postPortalAuthActivate
      tags: [Portal]
      summary: Activate an approved partner portal account.
      description: Uses the one-time activation token issued during application approval. Tokens expire after seven days; password reset provides email-based recovery.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerPortalActivation"
      responses:
        "200":
          description: Portal session.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerPortalSession"
        "400":
          $ref: "#/components/responses/PartnerError"
        "429":
          $ref: "#/components/responses/PartnerError"
  /portal/auth/login/:
    post:
      operationId: postPortalAuthLogin
      tags: [Portal]
      summary: Sign in to the partner developer portal.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerPortalLogin"
      responses:
        "200":
          description: Portal session.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerPortalSession"
        "401":
          $ref: "#/components/responses/PartnerError"
        "429":
          $ref: "#/components/responses/PartnerError"
  /portal/auth/password-reset/request/:
    post:
      operationId: postPortalAuthPasswordResetRequest
      tags: [Portal]
      summary: Request a partner portal password-reset link.
      description: Always returns the same accepted response for active and unknown email addresses.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerPortalPasswordResetRequest"
      responses:
        "202":
          description: Password-reset request accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerPortalPasswordResetAccepted"
        "429":
          $ref: "#/components/responses/PartnerError"
  /portal/auth/password-reset/confirm/:
    post:
      operationId: postPortalAuthPasswordResetConfirm
      tags: [Portal]
      summary: Set a new partner portal password.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerPortalPasswordResetConfirm"
      responses:
        "200":
          description: Password reset completed.
        "400":
          $ref: "#/components/responses/PartnerError"
  /delivery-quotes/:
    post:
      operationId: postDeliveryQuotes
      tags: [Deliveries]
      summary: Quote a delivery fee and serviceability.
      description: Requires `deliveries:quote` or `deliveries:create`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeliveryQuoteRequest"
      responses:
        "200":
          description: Delivery quote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeliveryQuote"
        "403":
          $ref: "#/components/responses/PartnerError"
  /deliveries/:
    get:
      operationId: getDeliveries
      tags: [Deliveries]
      summary: List partner deliveries.
      description: Requires `deliveries:read`.
      parameters:
        - $ref: "#/components/parameters/Environment"
        - $ref: "#/components/parameters/DeliveryStatus"
        - name: external_order_id
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 100
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
      responses:
        "200":
          description: Delivery page.
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/Delivery"
                  pagination:
                    $ref: "#/components/schemas/Pagination"
    post:
      operationId: postDeliveries
      tags: [Deliveries]
      summary: Create a delivery.
      description: Requires `deliveries:create` and an `Idempotency-Key` header.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeliveryCreate"
      responses:
        "201":
          description: Delivery created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
        "409":
          $ref: "#/components/responses/PartnerError"
  /deliveries/{delivery_id}/:
    get:
      operationId: getDeliveriesByDeliveryId
      tags: [Deliveries]
      summary: Read one delivery.
      description: Requires `deliveries:read`.
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      responses:
        "200":
          description: Delivery.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
  /deliveries/{delivery_id}/proofs/:
    get:
      operationId: getDeliveriesByDeliveryIdProofs
      tags: [Deliveries]
      summary: List delivery proofs.
      description: Requires `deliveries:read`.
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      responses:
        "200":
          description: Proof list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/DeliveryProof"
    post:
      operationId: postDeliveriesByDeliveryIdProofs
      tags: [Deliveries]
      summary: Record pickup, delivery, or return proof.
      description: Requires `deliveries:proof`.
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeliveryProofCreate"
      responses:
        "201":
          description: Proof recorded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeliveryProof"
  /deliveries/{delivery_id}/cancel/:
    post:
      operationId: postDeliveriesByDeliveryIdCancel
      tags: [Deliveries]
      summary: Cancel a delivery.
      description: Requires `deliveries:cancel` or `deliveries:create`.
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  example: customer_cancelled
      responses:
        "200":
          description: Cancelled delivery.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
  /deliveries/{delivery_id}/dispatch/retry/:
    post:
      operationId: postDeliveriesByDeliveryIdDispatchRetry
      tags: [Deliveries]
      summary: Retry dispatch for a deferred delivery.
      description: Requires `deliveries:dispatch` or `deliveries:create`.
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      responses:
        "200":
          description: Delivery after retry.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
  /sandbox/deliveries/{delivery_id}/simulate/:
    post:
      operationId: postSandboxDeliveriesByDeliveryIdSimulate
      tags: [Sandbox]
      summary: Simulate a sandbox delivery lifecycle transition.
      description: Requires a SANDBOX key with `sandbox:simulate`.
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SandboxSimulation"
      responses:
        "200":
          description: Simulated delivery.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
        "403":
          $ref: "#/components/responses/PartnerError"
        "409":
          $ref: "#/components/responses/PartnerError"
  /track/{tracking_token}/:
    get:
      operationId: getTrackByTrackingToken
      tags: [Deliveries]
      summary: Public delivery tracking.
      security: []
      parameters:
        - name: tracking_token
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Public tracking payload.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicTracking"
  /webhook-endpoints/:
    get:
      operationId: getWebhookEndpoints
      tags: [Webhooks]
      summary: List webhook endpoints.
      description: Requires `webhooks:manage`.
      responses:
        "200":
          description: Endpoint list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointList"
    post:
      operationId: postWebhookEndpoints
      tags: [Webhooks]
      summary: Create a webhook endpoint.
      description: Requires `webhooks:manage`. Returns `signing_secret` once.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookEndpointCreate"
      responses:
        "201":
          description: Endpoint created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/WebhookEndpoint"
                  - type: object
                    properties:
                      signing_secret:
                        type: string
  /webhook-endpoints/{endpoint_id}/rotate-secret/:
    post:
      operationId: postWebhookEndpointsByEndpointIdRotateSecret
      tags: [Webhooks]
      summary: Rotate webhook signing secret.
      description: Requires `webhooks:manage`. Returns `signing_secret` once.
      parameters:
        - $ref: "#/components/parameters/EndpointId"
      responses:
        "200":
          description: Endpoint with one-time signing secret.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/WebhookEndpoint"
                  - type: object
                    properties:
                      signing_secret:
                        type: string
  /webhook-endpoints/{endpoint_id}/test/:
    post:
      operationId: postWebhookEndpointsByEndpointIdTest
      tags: [Webhooks]
      summary: Queue or send a webhook test event.
      description: Requires `webhooks:manage`. Set `deliver_now=true` to attempt delivery immediately.
      parameters:
        - $ref: "#/components/parameters/EndpointId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookTest"
      responses:
        "201":
          description: Webhook test delivery created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDelivery"
  /webhook-deliveries/:
    get:
      operationId: getWebhookDeliveries
      tags: [Webhooks]
      summary: List webhook delivery attempts.
      description: Requires `webhooks:manage`.
      responses:
        "200":
          description: Webhook delivery list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDeliveryList"
  /webhook-deliveries/{webhook_delivery_id}/retry/:
    post:
      operationId: postWebhookDeliveriesByWebhookDeliveryIdRetry
      tags: [Webhooks]
      summary: Retry a webhook delivery.
      description: Requires `webhooks:manage`.
      parameters:
        - name: webhook_delivery_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Requeued webhook delivery.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDelivery"
  /billing/summary/:
    get:
      operationId: getBillingSummary
      tags: [Billing]
      summary: Read partner wallet and credit summary.
      description: Requires `billing:read`.
      responses:
        "200":
          description: Billing summary.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingSummary"
  /billing/topups/:
    get:
      operationId: getBillingTopups
      tags: [Billing]
      summary: List wallet top-ups.
      description: Requires `billing:read`.
      responses:
        "200":
          description: Top-up list.
    post:
      operationId: postBillingTopups
      tags: [Billing]
      summary: Initialize a Paystack top-up.
      description: Requires `billing:topup`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TopupInitialize"
      responses:
        "201":
          description: Top-up initialized.
  /billing/topups/verify/:
    post:
      operationId: postBillingTopupsVerify
      tags: [Billing]
      summary: Verify a Paystack top-up.
      description: Requires `billing:topup`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reference]
              properties:
                reference:
                  type: string
      responses:
        "200":
          description: Verified top-up.
  /billing/invoices/:
    get:
      operationId: getBillingInvoices
      tags: [Billing]
      summary: List invoices.
      description: Requires `billing:read`.
      responses:
        "200":
          description: Invoice list.
    post:
      operationId: postBillingInvoices
      tags: [Billing]
      summary: Issue an invoice for unsettled posted charges.
      description: Requires `billing:invoice`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceIssue"
      responses:
        "201":
          description: Invoice issued.
  /billing/invoices/{invoice_id}/:
    get:
      operationId: getBillingInvoicesByInvoiceId
      tags: [Billing]
      summary: Read one partner invoice.
      description: Requires `billing:read`.
      parameters:
        - $ref: "#/components/parameters/InvoiceId"
      responses:
        "200":
          description: Partner invoice and linked ledger entries.
        "404":
          $ref: "#/components/responses/PartnerError"
  /billing/invoices/{invoice_id}/mark-paid/:
    post:
      operationId: postBillingInvoicesByInvoiceIdMarkPaid
      tags: [Billing]
      summary: Record payment for a partner invoice.
      description: Requires `billing:invoice`.
      parameters:
        - $ref: "#/components/parameters/InvoiceId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                payment_reference:
                  type: string
                paid_at:
                  type: string
                  format: date-time
      responses:
        "200":
          description: Paid partner invoice.
        "409":
          $ref: "#/components/responses/PartnerError"
  /ledger-entries/:
    get:
      operationId: getLedgerEntries
      tags: [Billing]
      summary: List ledger entries.
      description: Requires `billing:read`.
      responses:
        "200":
          description: Ledger entry page.
  /ledger-entries/export.csv:
    get:
      operationId: getLedgerEntriesExportCsv
      tags: [Billing]
      summary: Export ledger entries as CSV.
      description: Requires `billing:read`.
      responses:
        "200":
          description: CSV export.
          content:
            text/csv:
              schema:
                type: string
  /audit-logs/:
    get:
      operationId: getAuditLogs
      tags: [Deliveries]
      summary: List recent API audit logs.
      description: Requires `audit:read`.
      responses:
        "200":
          description: Audit log list.
  /portal/me/:
    get:
      operationId: getPortalMe
      tags: [Portal]
      summary: Read authenticated portal memberships.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Memberships.
  /portal/profile/:
    get:
      operationId: getPortalProfile
      tags: [Portal]
      summary: Read partner profile.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Partner profile.
    patch:
      operationId: patchPortalProfile
      tags: [Portal]
      summary: Update support, technical, and finance contacts.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerProfileUpdate"
      responses:
        "200":
          description: Updated profile.
  /portal/usage/:
    get:
      operationId: getPortalUsage
      tags: [Portal]
      summary: Read usage analytics.
      security:
        - UserJwt: []
      parameters:
        - name: days
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 365
            default: 30
      responses:
        "200":
          description: Usage summary.
  /portal/readiness/:
    get:
      operationId: getPortalReadiness
      tags: [Portal]
      summary: Read go-live readiness checks for the signed-in partner.
      description: Returns evidence-backed checks, recommended checklist values, and whether live access activates automatically or requires the exception review set during verification.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Go-live readiness summary.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerReadiness"
  /portal/delivery-quotes/:
    post:
      operationId: postPortalDeliveryQuotes
      tags: [Portal]
      summary: Quote a sandbox delivery from the developer portal.
      description: Uses the signed-in portal session and forces the sandbox environment; no partner API key is required.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeliveryQuoteRequest"
      responses:
        "200":
          description: Sandbox delivery quote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeliveryQuote"
  /portal/deliveries/:
    get:
      operationId: getPortalDeliveries
      tags: [Portal]
      summary: List partner deliveries from the developer portal.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/Environment"
        - $ref: "#/components/parameters/DeliveryStatus"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 100
        - name: offset
          in: query
          schema:
            type: integer
            minimum: 0
            default: 0
      responses:
        "200":
          description: Delivery page.
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/Delivery"
                  pagination:
                    $ref: "#/components/schemas/Pagination"
    post:
      operationId: postPortalDeliveries
      tags: [Portal]
      summary: Create a sandbox test delivery from the developer portal.
      description: Forces `SANDBOX` and does not require an `Idempotency-Key` header because the portal session is the actor.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeliveryCreate"
      responses:
        "201":
          description: Sandbox delivery created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
  /portal/deliveries/{delivery_id}/:
    get:
      operationId: getPortalDeliveriesByDeliveryId
      tags: [Portal]
      summary: Read one partner delivery from the developer portal.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      responses:
        "200":
          description: Delivery.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
  /portal/deliveries/{delivery_id}/proofs/:
    get:
      operationId: getPortalDeliveriesByDeliveryIdProofs
      tags: [Portal]
      summary: List delivery proofs from the developer portal.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      responses:
        "200":
          description: Proof list.
    post:
      operationId: postPortalDeliveriesByDeliveryIdProofs
      tags: [Portal]
      summary: Record pickup, delivery, or return proof from the developer portal.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeliveryProofCreate"
      responses:
        "201":
          description: Proof recorded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeliveryProof"
        "409":
          $ref: "#/components/responses/PartnerError"
  /portal/deliveries/{delivery_id}/cancel/:
    post:
      operationId: postPortalDeliveriesByDeliveryIdCancel
      tags: [Portal]
      summary: Cancel a partner delivery from the developer portal.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  example: customer_cancelled
      responses:
        "200":
          description: Cancelled delivery.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
  /portal/deliveries/{delivery_id}/dispatch/retry/:
    post:
      operationId: postPortalDeliveriesByDeliveryIdDispatchRetry
      tags: [Portal]
      summary: Retry dispatch from the developer portal.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      responses:
        "200":
          description: Delivery after retry.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
        "409":
          $ref: "#/components/responses/PartnerError"
  /portal/deliveries/{delivery_id}/simulate/:
    post:
      operationId: postPortalDeliveriesByDeliveryIdSimulate
      tags: [Portal]
      summary: Simulate a sandbox delivery lifecycle step from the developer portal.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/DeliveryId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SandboxSimulation"
      responses:
        "200":
          description: Updated sandbox delivery.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Delivery"
  /portal/sandbox/reset/:
    post:
      operationId: postPortalSandboxReset
      tags: [Portal]
      summary: Reset the signed-in partner sandbox.
      description: Revokes active sandbox credentials, issues a fresh sandbox key, expires active sandbox quotes, and cancels non-terminal sandbox deliveries without mutating live data.
      security:
        - UserJwt: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerSandboxResetRequest"
      responses:
        "200":
          description: Sandbox reset summary and one-time fresh sandbox key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerSandboxResetResponse"
  /portal/audit-logs/:
    get:
      operationId: getPortalAuditLogs
      tags: [Portal]
      summary: List API request logs for the signed-in partner.
      security:
        - UserJwt: []
      responses:
        "200":
          description: API request log list.
  /portal/security/events/:
    get:
      operationId: getPortalSecurityEvents
      tags: [Portal]
      summary: List security activity for the signed-in partner user.
      description: Returns up to 50 of the current user's most recent partner-portal security events.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Security event list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerSecurityEventList"
  /portal/security/sessions/revoke/:
    post:
      operationId: postPortalSecuritySessionsRevoke
      tags: [Portal]
      summary: Sign the current partner user out of every portal session.
      description: Invalidates the user's current and previously issued partner-portal access tokens, including access tokens created from older refresh tokens.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Partner portal sessions revoked.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerSessionRevocation"
  /portal/billing/summary/:
    get:
      operationId: getPortalBillingSummary
      tags: [Portal]
      summary: Read wallet and credit summary for the signed-in partner.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Billing summary.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingSummary"
  /portal/billing/topups/:
    get:
      operationId: getPortalBillingTopups
      tags: [Portal]
      summary: List wallet top-ups from the developer portal.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Top-up list.
    post:
      operationId: postPortalBillingTopups
      tags: [Portal]
      summary: Initialize a Paystack wallet top-up from the developer portal.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TopupInitialize"
      responses:
        "201":
          description: Top-up initialized.
  /portal/billing/topups/verify/:
    post:
      operationId: postPortalBillingTopupsVerify
      tags: [Portal]
      summary: Verify a Paystack wallet top-up from the developer portal.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reference]
              properties:
                reference:
                  type: string
      responses:
        "200":
          description: Top-up verified.
  /portal/billing/invoices/:
    get:
      operationId: getPortalBillingInvoices
      tags: [Portal]
      summary: List partner invoices from the developer portal.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Invoice list.
  /portal/ledger-entries/:
    get:
      operationId: getPortalLedgerEntries
      tags: [Portal]
      summary: List partner ledger entries from the developer portal.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Ledger entry list.
  /portal/ledger-entries/export.csv:
    get:
      operationId: getPortalLedgerEntriesExportCsv
      tags: [Portal]
      summary: Export partner ledger entries as CSV from the developer portal.
      security:
        - UserJwt: []
      responses:
        "200":
          description: CSV ledger export.
          content:
            text/csv:
              schema:
                type: string
  /portal/go-live-requests/:
    get:
      operationId: getPortalGoLiveRequests
      tags: [Portal]
      summary: List partner go-live requests.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Go-live request list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/GoLiveRequest"
    post:
      operationId: postPortalGoLiveRequests
      tags: [Portal]
      summary: Activate live API access.
      description: Automatically approves verified standard-risk partners when all required readiness checks pass. Accounts marked for additional review during verification create a pending staff request instead. No request is created while required evidence is missing.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GoLiveRequestCreate"
      responses:
        "201":
          description: Live access approved automatically or queued for the verification-defined exception review.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GoLiveRequest"
        "409":
          $ref: "#/components/responses/PartnerError"
  /portal/team/:
    get:
      operationId: getPortalTeam
      tags: [Portal]
      summary: List team members.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Team member list.
    post:
      operationId: postPortalTeam
      tags: [Portal]
      summary: Invite or update a team member.
      description: Creates the membership, stores an activation token hash, and emails the invited user a portal activation link.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TeamMemberCreate"
      responses:
        "201":
          description: Team member created.
  /portal/team/{team_member_id}/:
    patch:
      operationId: patchPortalTeamByTeamMemberId
      tags: [Portal]
      summary: Change a partner team member role.
      description: Owners and admins can change roles. Only owners can grant or manage owner access, and users cannot change their own role.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/TeamMemberId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TeamMemberRoleUpdate"
      responses:
        "200":
          description: Updated team member.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamMember"
        "403":
          $ref: "#/components/responses/PartnerError"
        "409":
          $ref: "#/components/responses/PartnerError"
  /portal/team/{team_member_id}/disable/:
    post:
      operationId: postPortalTeamByTeamMemberIdDisable
      tags: [Portal]
      summary: Disable a partner team member.
      description: Revokes portal access immediately without changing API credentials.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/TeamMemberId"
      responses:
        "200":
          description: Disabled team member.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamMember"
        "409":
          $ref: "#/components/responses/PartnerError"
  /portal/team/{team_member_id}/reinvite/:
    post:
      operationId: postPortalTeamByTeamMemberIdReinvite
      tags: [Portal]
      summary: Restore access and send a new activation link.
      description: Replaces any previous activation token and emails the member a new link.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/TeamMemberId"
      responses:
        "200":
          description: Reinvited team member.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamMember"
  /portal/credentials/:
    get:
      operationId: getPortalCredentials
      tags: [Portal]
      summary: List API credentials.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Credential list.
    post:
      operationId: postPortalCredentials
      tags: [Portal]
      summary: Issue an API credential.
      description: Returns `api_key` once.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CredentialIssue"
      responses:
        "201":
          description: Credential issued.
  /portal/credentials/{credential_id}/revoke/:
    post:
      operationId: postPortalCredentialsByCredentialIdRevoke
      tags: [Portal]
      summary: Revoke an API credential.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/CredentialId"
      responses:
        "200":
          description: Revoked credential.
  /portal/credentials/{credential_id}/rotate/:
    post:
      operationId: postPortalCredentialsByCredentialIdRotate
      tags: [Portal]
      summary: Rotate an API credential.
      description: Revokes the old credential and returns the replacement `api_key` once.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/CredentialId"
      responses:
        "201":
          description: Replacement credential with a one-time raw key.
  /portal/webhooks/:
    get:
      operationId: getPortalWebhooks
      tags: [Portal]
      summary: List webhook endpoints.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Endpoint list.
    post:
      operationId: postPortalWebhooks
      tags: [Portal]
      summary: Create webhook endpoint from portal.
      security:
        - UserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookEndpointCreate"
      responses:
        "201":
          description: Endpoint created.
  /portal/webhooks/{endpoint_id}/:
    get:
      operationId: getPortalWebhooksByEndpointId
      tags: [Portal]
      summary: Read a webhook endpoint.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/EndpointId"
      responses:
        "200":
          description: Webhook endpoint.
    patch:
      operationId: patchPortalWebhooksByEndpointId
      tags: [Portal]
      summary: Update a webhook endpoint.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/EndpointId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
                event_types:
                  type: array
                  items:
                    type: string
                status:
                  type: string
                  enum: [ACTIVE, DISABLED]
      responses:
        "200":
          description: Updated webhook endpoint.
  /portal/webhooks/{endpoint_id}/rotate-secret/:
    post:
      operationId: postPortalWebhooksByEndpointIdRotateSecret
      tags: [Portal]
      summary: Rotate a webhook signing secret.
      description: Returns the new `signing_secret` once.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/EndpointId"
      responses:
        "200":
          description: Webhook endpoint with a one-time signing secret.
  /portal/webhooks/{endpoint_id}/test/:
    post:
      operationId: postPortalWebhooksByEndpointIdTest
      tags: [Portal]
      summary: Queue or send a portal webhook test event.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/EndpointId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookTest"
      responses:
        "201":
          description: Webhook test delivery created.
  /portal/webhook-deliveries/:
    get:
      operationId: getPortalWebhookDeliveries
      tags: [Portal]
      summary: List webhook delivery attempts.
      security:
        - UserJwt: []
      responses:
        "200":
          description: Delivery attempt list.
  /portal/webhook-deliveries/{webhook_delivery_id}/retry/:
    post:
      operationId: postPortalWebhookDeliveriesByWebhookDeliveryIdRetry
      tags: [Portal]
      summary: Retry a failed webhook delivery.
      security:
        - UserJwt: []
      parameters:
        - $ref: "#/components/parameters/WebhookDeliveryId"
      responses:
        "200":
          description: Requeued webhook delivery.
  /admin/go-live-requests/:
    get:
      operationId: getAdminGoLiveRequests
      tags: [Everest Review]
      summary: List partner go-live requests for Everest review.
      security:
        - UserJwt: []
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [PENDING, APPROVED, REJECTED]
        - name: partner_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Go-live request list.
  /admin/go-live-requests/{live_request_id}/approve/:
    post:
      operationId: postAdminGoLiveRequestsByLiveRequestIdApprove
      tags: [Everest Review]
      summary: Approve live API access and optionally issue a live key.
      security:
        - UserJwt: []
      parameters:
        - name: live_request_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GoLiveRequestApprove"
      responses:
        "200":
          description: Approved request with one-time live API key when issued.
  /admin/go-live-requests/{live_request_id}/reject/:
    post:
      operationId: postAdminGoLiveRequestsByLiveRequestIdReject
      tags: [Everest Review]
      summary: Reject live API access.
      security:
        - UserJwt: []
      parameters:
        - name: live_request_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                review_notes:
                  type: string
      responses:
        "200":
          description: Rejected request.
components:
  securitySchemes:
    PartnerApiKey:
      type: apiKey
      in: header
      name: X-Everest-Partner-Key
    UserJwt:
      type: http
      scheme: bearer
      bearerFormat: JWT
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        maxLength: 180
    DeliveryId:
      name: delivery_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    PartnerId:
      name: partner_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    CredentialId:
      name: credential_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    InvoiceId:
      name: invoice_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    WebhookDeliveryId:
      name: webhook_delivery_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    TeamMemberId:
      name: team_member_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    EndpointId:
      name: endpoint_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    Environment:
      name: environment
      in: query
      schema:
        $ref: "#/components/schemas/Environment"
    DeliveryStatus:
      name: status
      in: query
      schema:
        $ref: "#/components/schemas/DeliveryStatus"
  responses:
    PartnerError:
      description: Partner API error envelope.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PartnerError"
  schemas:
    Environment:
      type: string
      enum: [SANDBOX, LIVE]
    DeliveryStatus:
      type: string
      enum:
        - REQUESTED
        - DISPATCH_DEFERRED
        - DISPATCHING
        - OFFERED
        - ASSIGNED
        - IN_PROGRESS
        - COMPLETED
        - CANCELLED
        - NO_DRIVER
        - FAILED
    ServiceType:
      type: string
      enum: [DELIVERY_BIKE, DELIVERY_CAR]
    Location:
      type: object
      required: [label, latitude, longitude]
      properties:
        label:
          type: string
        latitude:
          type: string
          example: "6.5000000000"
        longitude:
          type: string
          example: "3.3700000000"
        city:
          type: string
          default: Lagos
    Package:
      type: object
      properties:
        description:
          type: string
        weight_kg:
          type: string
          example: "1.20"
    Recipient:
      type: object
      required: [name, phone]
      properties:
        name:
          type: string
        phone:
          type: string
        email:
          type: string
          format: email
    DeliveryQuoteRequest:
      type: object
      required: [pickup, dropoff]
      properties:
        service_type:
          $ref: "#/components/schemas/ServiceType"
        pickup:
          $ref: "#/components/schemas/Location"
        dropoff:
          $ref: "#/components/schemas/Location"
        package:
          $ref: "#/components/schemas/Package"
    DeliveryQuote:
      type: object
      properties:
        quote_id:
          type: string
          format: uuid
        environment:
          $ref: "#/components/schemas/Environment"
        service_type:
          $ref: "#/components/schemas/ServiceType"
        currency:
          type: string
          example: NGN
        estimated_amount:
          type: string
        estimated_distance_km:
          type: string
        estimated_duration_minutes:
          type: string
        quote_expires_at:
          type: string
          format: date-time
        quote_expires_in_seconds:
          type: integer
        dispatchable:
          type: boolean
        quote_source:
          type: string
        price_breakdown:
          type: object
    DeliveryCreate:
      allOf:
        - $ref: "#/components/schemas/DeliveryQuoteRequest"
        - type: object
          required: [external_order_id, recipient]
          properties:
            quote_id:
              type: string
              format: uuid
            external_order_id:
              type: string
            recipient:
              $ref: "#/components/schemas/Recipient"
            payment_owner:
              type: string
              enum: [PARTNER, CUSTOMER]
              default: PARTNER
            delivery_instructions:
              type: string
            dispatch:
              type: boolean
              default: true
            metadata:
              type: object
    Delivery:
      type: object
      properties:
        delivery_id:
          type: string
          format: uuid
        tracking_url:
          type: string
        external_order_id:
          type: string
        environment:
          $ref: "#/components/schemas/Environment"
        service_type:
          $ref: "#/components/schemas/ServiceType"
        status:
          $ref: "#/components/schemas/DeliveryStatus"
        currency:
          type: string
        quoted_amount:
          type: string
        final_amount:
          type: string
          nullable: true
        metadata:
          type: object
        core_status_snapshot:
          type: object
        proofs:
          type: array
          items:
            $ref: "#/components/schemas/DeliveryProof"
        ledger_entries:
          type: array
          items:
            type: object
    DeliveryProofCreate:
      type: object
      required: [proof_type]
      properties:
        proof_type:
          type: string
          enum: [PICKUP, DELIVERY, RETURN]
        recorded_at:
          type: string
          format: date-time
        photo_url:
          type: string
        signature_name:
          type: string
        notes:
          type: string
        metadata:
          type: object
    DeliveryProof:
      type: object
      properties:
        proof_id:
          type: string
          format: uuid
        proof_type:
          type: string
        status:
          type: string
        recorded_by:
          type: string
        recorded_at:
          type: string
          format: date-time
    SandboxSimulation:
      type: object
      required: [action]
      properties:
        action:
          type: string
          enum: [ASSIGN_DRIVER, PICKUP, COMPLETE, FAIL, NO_DRIVER]
        driver_id:
          type: string
          default: sandbox-driver-1
        vehicle_id:
          type: string
          default: sandbox-bike-1
        reason:
          type: string
        recorded_at:
          type: string
          format: date-time
        metadata:
          type: object
    PublicTracking:
      type: object
      properties:
        delivery_id:
          type: string
          format: uuid
        tracking_url:
          type: string
        external_order_id:
          type: string
        status:
          $ref: "#/components/schemas/DeliveryStatus"
        pickup_label:
          type: string
        dropoff_label:
          type: string
    WebhookEndpointCreate:
      type: object
      required: [url, events]
      properties:
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
          example: [partner.delivery.*]
    WebhookEndpoint:
      type: object
      properties:
        id:
          type: string
          format: uuid
        url:
          type: string
        events:
          type: array
          items:
            type: string
        status:
          type: string
        signing_secret_version:
          type: integer
    WebhookEndpointList:
      type: object
      properties:
        results:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEndpoint"
    WebhookDelivery:
      type: object
      properties:
        webhook_delivery_id:
          type: string
          format: uuid
        endpoint_id:
          type: string
          format: uuid
        event_type:
          type: string
        status:
          type: string
        attempt_count:
          type: integer
        last_error:
          type: string
    WebhookTest:
      type: object
      properties:
        event_type:
          type: string
          example: partner.delivery.created
        payload:
          type: object
          example:
            external_order_id: TEST-WEBHOOK-ORDER
        deliver_now:
          type: boolean
          default: false
    WebhookDeliveryList:
      type: object
      properties:
        results:
          type: array
          items:
            $ref: "#/components/schemas/WebhookDelivery"
    PartnerSecurityEvent:
      type: object
      properties:
        security_event_id:
          type: string
          format: uuid
        event_type:
          type: string
          enum:
            - LOGIN_SUCCEEDED
            - LOGIN_FAILED
            - LOGIN_RATE_LIMITED
            - ACTIVATION_SUCCEEDED
            - ACTIVATION_FAILED
            - PASSWORD_RESET_REQUESTED
            - PASSWORD_RESET_COMPLETED
            - SESSIONS_REVOKED
            - TEAM_ROLE_CHANGED
            - TEAM_ACCESS_DISABLED
            - TEAM_ACCESS_RESTORED
        outcome:
          type: string
          enum: [SUCCESS, FAILURE, INFO]
        user_name:
          type: string
        actor_name:
          type: string
        request_id:
          type: string
        ip_address:
          type: string
        user_agent:
          type: string
        metadata:
          type: object
        created_at:
          type: string
          format: date-time
    PartnerSecurityEventList:
      type: object
      properties:
        results:
          type: array
          items:
            $ref: "#/components/schemas/PartnerSecurityEvent"
    PartnerSessionRevocation:
      type: object
      required: [status, revoked_at]
      properties:
        status:
          type: string
          enum: [sessions_revoked]
        revoked_at:
          type: string
          format: date-time
    BillingSummary:
      type: object
      properties:
        wallet_balance:
          type: string
        credit_limit:
          type: string
        available_credit:
          type: string
        spendable_balance:
          type: string
        billing_mode:
          type: string
    TopupInitialize:
      type: object
      required: [amount]
      properties:
        amount:
          type: string
        callback_url:
          type: string
          format: uri
        metadata:
          type: object
    InvoiceIssue:
      type: object
      required: [period_start, period_end]
      properties:
        period_start:
          type: string
          format: date-time
        period_end:
          type: string
          format: date-time
        due_at:
          type: string
          format: date-time
        metadata:
          type: object
    PartnerApplicationCreate:
      type: object
      required: [business_name, contact_name, contact_email, contact_phone]
      properties:
        business_name:
          type: string
        business_type:
          type: string
          enum: [RESTAURANT, ECOMMERCE, PHARMACY, SUPERMARKET, OTHER]
        contact_name:
          type: string
        contact_email:
          type: string
          format: email
        contact_phone:
          type: string
        website:
          type: string
          format: uri
        requested_services:
          type: array
          items:
            $ref: "#/components/schemas/ServiceType"
        requested_cities:
          type: array
          items:
            type: string
        expected_monthly_deliveries:
          type: integer
        legal_name:
          type: string
          description: Registered legal business name for business verification review.
        trading_name:
          type: string
        registration_type:
          type: string
          enum: [REGISTERED_COMPANY, BUSINESS_NAME, SOLE_PROPRIETOR, PARTNERSHIP, NON_PROFIT, UNREGISTERED]
        registration_number:
          type: string
          description: CAC RC/BN or equivalent registration reference where applicable.
        tax_identification_number:
          type: string
        registered_address:
          type: string
        operating_address:
          type: string
        business_description:
          type: string
        owner_name:
          type: string
        owner_role:
          type: string
        owner_email:
          type: string
          format: email
        owner_phone:
          type: string
        identity_type:
          type: string
          enum: [NIN, BVN, PASSPORT, DRIVERS_LICENSE, VOTERS_CARD]
        identity_last4:
          type: string
          description: Last four digits only. Do not send full identity numbers to this endpoint.
          maxLength: 4
        identity_verification_consent:
          type: boolean
        compliance_contact_email:
          type: string
          format: email
        verification_documents:
          type: object
          additionalProperties:
            type: object
    PartnerApplication:
      allOf:
        - $ref: "#/components/schemas/PartnerApplicationCreate"
        - type: object
          properties:
            application_id:
              type: string
              format: uuid
            status:
              type: string
              enum: [PENDING, APPROVED, REJECTED]
            partner_id:
              type: string
              format: uuid
              nullable: true
            reviewed_at:
              type: string
              format: date-time
              nullable: true
            review_notes:
              type: string
            verification_status:
              type: string
              enum: [NOT_STARTED, PENDING, NEEDS_INFO, VERIFIED, REJECTED]
            verification_notes:
              type: string
            risk_tier:
              type: string
              enum: [LOW, STANDARD, HIGH]
            metadata:
              type: object
    PartnerApplicationStatus:
      type: object
      properties:
        application_id:
          type: string
          format: uuid
        business_name:
          type: string
        business_type:
          type: string
          enum: [RESTAURANT, ECOMMERCE, PHARMACY, SUPERMARKET, OTHER]
        contact_email:
          type: string
          format: email
        requested_services:
          type: array
          items:
            $ref: "#/components/schemas/ServiceType"
        requested_cities:
          type: array
          items:
            type: string
        expected_monthly_deliveries:
          type: integer
        legal_name:
          type: string
        registration_type:
          type: string
          enum: [REGISTERED_COMPANY, BUSINESS_NAME, SOLE_PROPRIETOR, PARTNERSHIP, NON_PROFIT, UNREGISTERED]
        registration_number:
          type: string
        verification_status:
          type: string
          enum: [NOT_STARTED, PENDING, NEEDS_INFO, VERIFIED, REJECTED]
        risk_tier:
          type: string
          enum: [LOW, STANDARD, HIGH]
        status:
          type: string
          enum: [PENDING, APPROVED, REJECTED]
        submitted_at:
          type: string
          format: date-time
        reviewed_at:
          type: string
          format: date-time
          nullable: true
        review_notes:
          type: string
        next_step:
          type: string
    PartnerApplicationApprove:
      type: object
      properties:
        allowed_services:
          type: array
          items:
            $ref: "#/components/schemas/ServiceType"
        allowed_cities:
          type: array
          items:
            type: string
        billing_mode:
          type: string
          enum: [PREPAID, POSTPAID]
          default: PREPAID
        credit_limit:
          type: string
          default: "0.00"
        issue_sandbox_key:
          type: boolean
          default: true
        verification_status:
          type: string
          enum: [NOT_STARTED, PENDING, NEEDS_INFO, VERIFIED, REJECTED]
          default: VERIFIED
        risk_tier:
          type: string
          enum: [LOW, STANDARD, HIGH]
          default: STANDARD
        verification_notes:
          type: string
        review_notes:
          type: string
    PartnerApplicationReject:
      type: object
      properties:
        review_notes:
          type: string
        verification_status:
          type: string
          enum: [NEEDS_INFO, REJECTED]
          default: REJECTED
        verification_notes:
          type: string
    PartnerApplicationApproval:
      type: object
      properties:
        application:
          $ref: "#/components/schemas/PartnerApplication"
        partner:
          type: object
        credential:
          type: object
          nullable: true
        api_key:
          type: string
          description: One-time raw sandbox API key, returned only when `issue_sandbox_key` is true.
        portal_activation_token:
          type: string
          description: One-time token used by the approved partner owner to activate portal access.
    PartnerPortalActivation:
      type: object
      required: [token, password]
      properties:
        token:
          type: string
          description: One-time portal activation token returned during staff approval.
        password:
          type: string
          minLength: 8
          maxLength: 128
          writeOnly: true
    PartnerPortalLogin:
      type: object
      required: [email_or_phone, password]
      properties:
        email_or_phone:
          type: string
        password:
          type: string
          maxLength: 128
          writeOnly: true
    PartnerPortalPasswordResetRequest:
      type: object
      required: [email]
      properties:
        email:
          type: string
          format: email
    PartnerPortalPasswordResetAccepted:
      type: object
      properties:
        status:
          type: string
          enum: [accepted]
        message:
          type: string
    PartnerPortalPasswordResetConfirm:
      type: object
      required: [uid, token, password]
      properties:
        uid:
          type: string
        token:
          type: string
        password:
          type: string
          minLength: 8
          maxLength: 128
          writeOnly: true
    PartnerPortalSession:
      type: object
      properties:
        access:
          type: string
        refresh:
          type: string
        user:
          type: object
          properties:
            name:
              type: string
            email:
              type: string
            phone:
              type: string
            role:
              type: string
              enum: [OWNER, ADMIN, DEVELOPER, FINANCE, SUPPORT]
            partner_id:
              type: string
              format: uuid
            partner_name:
              type: string
        membership:
          type: object
        partner:
          type: object
    GoLiveRequestCreate:
      type: object
      properties:
        requested_services:
          type: array
          items:
            $ref: "#/components/schemas/ServiceType"
        requested_cities:
          type: array
          items:
            type: string
        expected_monthly_deliveries:
          type: integer
          minimum: 0
        production_webhook_url:
          type: string
          format: uri
        checklist:
          type: object
          properties:
            sandbox_delivery_completed:
              type: boolean
            webhook_signature_verified:
              type: boolean
            finance_reconciliation_tested:
              type: boolean
        notes:
          type: string
        metadata:
          type: object
    GoLiveRequestApprove:
      type: object
      properties:
        scopes:
          type: array
          items:
            type: string
        allowed_ips:
          type: array
          items:
            type: string
        expires_at:
          type: string
          format: date-time
        issue_live_key:
          type: boolean
          default: true
        review_notes:
          type: string
    GoLiveRequest:
      allOf:
        - $ref: "#/components/schemas/GoLiveRequestCreate"
        - type: object
          properties:
            live_request_id:
              type: string
              format: uuid
            partner_id:
              type: string
              format: uuid
            status:
              type: string
              enum: [PENDING, APPROVED, REJECTED]
            reviewed_at:
              type: string
              format: date-time
              nullable: true
            review_notes:
              type: string
            approved_credential:
              type: object
    PartnerProfileUpdate:
      type: object
      properties:
        support_contact_name:
          type: string
        support_contact_email:
          type: string
          format: email
        support_contact_phone:
          type: string
        technical_contact_email:
          type: string
          format: email
        finance_contact_email:
          type: string
          format: email
        support_metadata:
          type: object
    TeamMemberCreate:
      type: object
      required: [phone]
      properties:
        phone:
          type: string
        email:
          type: string
          format: email
        first_name:
          type: string
        last_name:
          type: string
        role:
          type: string
          enum: [OWNER, ADMIN, DEVELOPER, FINANCE, SUPPORT]
    TeamMemberRoleUpdate:
      type: object
      required: [role]
      properties:
        role:
          type: string
          enum: [OWNER, ADMIN, DEVELOPER, FINANCE, SUPPORT]
    TeamMember:
      type: object
      properties:
        team_member_id:
          type: string
          format: uuid
        partner_id:
          type: string
          format: uuid
        user_id:
          type: string
          format: uuid
        user_name:
          type: string
        user_email:
          type: string
          format: email
        user_phone:
          type: string
        role:
          type: string
          enum: [OWNER, ADMIN, DEVELOPER, FINANCE, SUPPORT]
        status:
          type: string
          enum: [ACTIVE, INVITED, DISABLED]
        metadata:
          type: object
    CredentialIssue:
      type: object
      properties:
        name:
          type: string
        environment:
          $ref: "#/components/schemas/Environment"
        scopes:
          type: array
          items:
            type: string
        allowed_ips:
          type: array
          items:
            type: string
        expires_at:
          type: string
          format: date-time
    PartnerSandboxResetRequest:
      type: object
      properties:
        credential_name:
          type: string
          default: Sandbox key
    PartnerSandboxResetResponse:
      type: object
      properties:
        environment:
          $ref: "#/components/schemas/Environment"
        credential:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
            key_prefix:
              type: string
            environment:
              $ref: "#/components/schemas/Environment"
            scopes:
              type: array
              items:
                type: string
            allowed_ips:
              type: array
              items:
                type: string
            status:
              type: string
        api_key:
          type: string
          description: One-time raw sandbox API key.
        revoked_credentials_count:
          type: integer
        expired_quotes_count:
          type: integer
        cancelled_deliveries_count:
          type: integer
        voided_ledger_entries_count:
          type: integer
        revoked_credential_ids:
          type: array
          items:
            type: string
            format: uuid
    PartnerReadiness:
      type: object
      properties:
        partner_id:
          type: string
          format: uuid
        partner_name:
          type: string
        ready_for_go_live:
          type: boolean
        live_access_policy:
          type: object
          required: [automatic_when_ready, manual_review_required, reason]
          properties:
            automatic_when_ready:
              type: boolean
            manual_review_required:
              type: boolean
            reason:
              type: string
        generated_at:
          type: string
          format: date-time
        recommended_checklist:
          type: object
          properties:
            kyb_verified:
              type: boolean
            sandbox_delivery_completed:
              type: boolean
            webhook_signature_verified:
              type: boolean
            finance_reconciliation_tested:
              type: boolean
        checks:
          type: array
          items:
            type: object
            properties:
              key:
                type: string
              label:
                type: string
              required:
                type: boolean
              passed:
                type: boolean
              status:
                type: string
                enum: [PASS, MISSING]
              detail:
                type: string
              evidence:
                type: object
    Pagination:
      type: object
      properties:
        count:
          type: integer
        limit:
          type: integer
        offset:
          type: integer
        next_offset:
          type: integer
          nullable: true
        previous_offset:
          type: integer
          nullable: true
    PartnerError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: object
            request_id:
              type: string
