openapi: 3.1.0
info:
  title: paytrack API
  version: 1.0.0
  description: Versioned public API for paytrack customer, track, payment, reminder, report, ledger, and webhook workflows.
servers:
  - url: https://paytrack.ng/api/v1
security:
  - bearerAuth: []
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: ptrk_test or ptrk_live API key
  schemas:
    ApiSuccess:
      type: object
      required: [success, data, request_id]
      properties:
        success: { type: boolean, const: true }
        data: { type: object }
        request_id: { type: string, example: req_abc123 }
    ApiError:
      type: object
      required: [success, error, request_id]
      properties:
        success: { type: boolean, const: false }
        error:
          type: object
          required: [code, message]
          properties:
            code: { type: string, example: validation_error }
            message: { type: string }
            details: { type: object }
        request_id: { type: string }
    CustomerCreate:
      type: object
      required: [name, phone]
      properties:
        name: { type: string, example: ABC Furniture }
        email: { type: string, format: email, example: customer@example.com }
        phone: { type: string, example: "+2348012345678" }
        notes: { type: string }
    TrackCreate:
      type: object
      required: [customerId, title, amount]
      properties:
        customerId: { type: string }
        title: { type: string, example: Motorcycle Installment }
        amount: { type: integer, example: 1200000 }
        currency: { type: string, example: NGN }
        installments: { type: integer, example: 12 }
    PaymentRecord:
      type: object
      required: [trackId, amount]
      properties:
        trackId: { type: string }
        amount: { type: integer, example: 100000 }
        method: { type: string, example: cash }
        paidAt: { type: string, format: date-time }
    CollectionScheduleCreate:
      type: object
      required: [target_type, frequency, fixed_amount, start_date]
      properties:
        target_type:
          type: string
          enum: [payment_plan, contribution_member, member_savings_member, generic_collection]
        target_id: { type: string, nullable: true }
        frequency:
          type: string
          enum: [one_time, daily, weekly, monthly, flexible, custom, installment_sequence]
        collection_method:
          type: string
          enum: [manual, reminder_only, paystack_checkout, monnify_virtual_account, auto_debit, mixed]
          default: manual
        fixed_amount: { type: integer, example: 25000 }
        start_date: { type: string, format: date-time }
        end_date: { type: string, format: date-time, nullable: true }
        interval: { type: integer, example: 1 }
    PaymentMandateCreate:
      type: object
      required: [payer_type, provider]
      properties:
        payer_type: { type: string, example: customer }
        payer_id: { type: string, nullable: true }
        provider: { type: string, example: paystack }
        schedule_id: { type: string, nullable: true }
    PaymentMandateRevoke:
      type: object
      properties:
        reason: { type: string, example: Customer withdrew consent }
  responses:
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ApiError" }
    RateLimited:
      description: Rate limit exceeded.
      headers:
        X-RateLimit-Limit: { schema: { type: string } }
        X-RateLimit-Remaining: { schema: { type: string } }
        X-RateLimit-Reset: { schema: { type: string } }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ApiError" }
paths:
  /account:
    get:
      summary: Get authenticated API account context.
      responses:
        "200": { description: Account context }
        "401": { $ref: "#/components/responses/Unauthorized" }
  /account/usage:
    get:
      summary: Get current API usage.
      responses:
        "200": { description: API usage }
  /account/subscription:
    get:
      summary: Get subscription API access model.
      responses:
        "200": { description: Subscription access }
  /customers:
    get:
      summary: List customers
      responses:
        "200": { description: Customer list }
    post:
      summary: Create customer
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CustomerCreate" }
      responses:
        "201": { description: Created customer }
  /customers/{id}:
    get:
      summary: Retrieve customer
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Customer }
    patch:
      summary: Update customer
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Updated customer }
    delete:
      summary: Archive customer
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Archived customer }
  /tracks:
    get:
      summary: List tracks
      responses:
        "200": { description: Track list }
    post:
      summary: Create track
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/TrackCreate" }
      responses:
        "201": { description: Created track }
  /tracks/{id}:
    get:
      summary: Retrieve track
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Track }
    patch:
      summary: Update track
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Updated track }
    delete:
      summary: Archive track
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Archived track }
  /payments:
    get:
      summary: List payments
      responses:
        "200": { description: Payment list }
    post:
      summary: Record payment
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PaymentRecord" }
      responses:
        "201": { description: Recorded payment }
  /payments/{id}:
    get:
      summary: Retrieve payment
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Payment }
  /tracks/{id}/payments:
    get:
      summary: List payments for a track
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Track payments }
  /reminders:
    get: { summary: List reminders, responses: { "200": { description: Reminders } } }
    post: { summary: Create reminder, responses: { "202": { description: Queued } } }
  /reminders/send:
    post: { summary: Send reminder, responses: { "202": { description: Queued } } }
  /reminders/logs:
    get: { summary: Reminder logs, responses: { "200": { description: Logs } } }
  /reports/summary:
    get: { summary: Summary report, responses: { "200": { description: Summary } } }
  /reports/collections:
    get: { summary: Collections report, responses: { "200": { description: Collections } } }
  /reports/overdue:
    get: { summary: Overdue report, responses: { "200": { description: Overdue accounts } } }
  /reports/export:
    post: { summary: Queue report export, responses: { "202": { description: Queued } } }
  /ledger:
    get: { summary: Workspace ledger, responses: { "200": { description: Ledger events } } }
  /customers/{id}/ledger:
    get:
      summary: Customer ledger
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Ledger events } }
  /tracks/{id}/ledger:
    get:
      summary: Track ledger
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Ledger events } }
  /webhooks:
    get: { summary: List webhook endpoints, responses: { "200": { description: Webhooks } } }
    post: { summary: Create webhook endpoint, responses: { "201": { description: Webhook endpoint and signing secret } } }
  /webhooks/{id}:
    patch:
      summary: Update webhook endpoint
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Updated webhook } }
    delete:
      summary: Disable webhook endpoint
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Disabled webhook } }
  /webhooks/{id}/test:
    post:
      summary: Queue a webhook test event
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { "202": { description: Queued } }
  /collection-schedules:
    get:
      summary: List collection schedules
      description: Lists provider-neutral collection schedules for the authenticated workspace.
      responses:
        "200": { description: Collection schedules }
        "401": { $ref: "#/components/responses/Unauthorized" }
    post:
      summary: Create collection schedule
      description: Creates a schedule and generates initial obligations. Direct debit execution is provider approval-gated.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CollectionScheduleCreate" }
      responses:
        "201": { description: Created schedule }
  /collection-schedules/{id}:
    get:
      summary: Retrieve collection schedule
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Collection schedule }
    patch:
      summary: Run a collection schedule action
      description: Supported actions include activate, pause, resume, cancel, generate_obligations, and sync_payment_plan.
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum: [activate, pause, resume, cancel, generate_obligations, sync_payment_plan]
      responses:
        "200": { description: Updated schedule or action result }
  /payment-mandates:
    get:
      summary: List payment mandates
      responses:
        "200": { description: Payment mandates }
    post:
      summary: Create payment mandate consent record
      description: Returns a one-time consent URL. Provider-side auto debit remains inactive until provider approval is configured.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PaymentMandateCreate" }
      responses:
        "201": { description: Created pending mandate }
  /payment-mandates/{id}:
    get:
      summary: Retrieve payment mandate
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Payment mandate }
  /payment-mandates/{id}/revoke:
    post:
      summary: Revoke payment mandate
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      requestBody:
        required: false
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PaymentMandateRevoke" }
      responses:
        "200": { description: Revoked mandate }
  /collection-attempts:
    get:
      summary: List scheduled collection attempts
      responses:
        "200": { description: Collection attempts }
  /collection-attempts/{id}:
    get:
      summary: Retrieve collection attempt
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Collection attempt }
