openapi: 3.1.0
info:
  title: MailThisForMe API
  version: 0.1.0
  description: >
    MVP API foundation for quoting and queueing physical mail jobs.
    Job creation is currently test-mode only and does not send mail.
servers:
  - url: https://api.mailthisforme.com
    description: Production API
  - url: https://dev.api.mailthisforme.com
    description: Development API
security:
  - bearerApiKey: []
tags:
  - name: Health
  - name: Quotes
  - name: Mail
  - name: Balance
paths:
  /health:
    get:
      tags: [Health]
      summary: Check API health
      security: []
      responses:
        '200':
          description: The application and required dependencies are healthy.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
        '503':
          description: One or more required dependencies failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
  /v1/mail/quote:
    post:
      tags: [Quotes]
      summary: Quote a mail job by page count
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [page_count]
              properties:
                page_count:
                  type: integer
                  minimum: 1
                  maximum: 10
                  example: 3
      responses:
        '200':
          description: Current quote for the requested page count.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Quote'
        '422':
          description: The request body failed validation.
  /v1/mail:
    post:
      tags: [Mail]
      summary: Create a queued mail job stub
      description: >
        Creates a test-mode queued mail job record. The endpoint does not
        download documents, charge balance, print, or send mail yet.
      security:
        - bearerApiKey: []
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateMailJobRequest'
      responses:
        '201':
          description: Mail job queued in test mode.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateMailJobResponse'
        '400':
          description: Missing Idempotency-Key header.
        '401':
          description: Missing, invalid, or revoked API key.
        '409':
          description: Idempotency key was reused with a different payload.
        '422':
          description: The request body failed validation.
  /v1/mail/{id}:
    get:
      tags: [Mail]
      summary: Get mail job status
      description: >
        Returns public status fields for a mail job visible to the
        authenticated API key context. The MVP ownership scope is the
        API key user_id; unowned API keys can only access unowned jobs.
      security:
        - bearerApiKey: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: mail_123
      responses:
        '200':
          description: Mail job status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailJobStatusResponse'
        '401':
          description: Missing, invalid, or revoked API key.
        '404':
          description: Mail job was not found for the authenticated API key context.
  /v1/mail/{id}/cancel:
    post:
      tags: [Mail]
      summary: Cancel a queued mail job
      description: >
        Cancels a mail job only while it is queued. This endpoint does not
        issue refunds or touch ledger entries yet.
      security:
        - bearerApiKey: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            example: mail_123
      responses:
        '200':
          description: Mail job was cancelled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailJobStatusResponse'
        '401':
          description: Missing, invalid, or revoked API key.
        '404':
          description: Mail job was not found for the authenticated API key context.
        '409':
          description: Mail job is no longer queued and cannot be cancelled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CancelConflictResponse'
  /v1/balance:
    get:
      tags: [Balance]
      summary: Planned balance endpoint
      description: Planned endpoint; not implemented yet.
      security:
        - bearerApiKey: []
      responses:
        '501':
          description: Planned endpoint.
  /v1/transactions:
    get:
      tags: [Balance]
      summary: Planned transaction history endpoint
      description: Planned endpoint; not implemented yet.
      security:
        - bearerApiKey: []
      responses:
        '501':
          description: Planned endpoint.
components:
  securitySchemes:
    bearerApiKey:
      type: http
      scheme: bearer
      bearerFormat: MailThisForMe API key
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        maxLength: 128
      description: Stable key for safely retrying POST /v1/mail.
  schemas:
    HealthResponse:
      type: object
      required: [ok]
      properties:
        ok:
          type: boolean
          example: true
    Quote:
      type: object
      required: [page_count, price_cents, currency, tier, max_pages]
      properties:
        page_count:
          type: integer
          example: 3
        price_cents:
          type: integer
          example: 225
        currency:
          type: string
          example: USD
        tier:
          type: string
          example: standard_1_4
        max_pages:
          type: integer
          example: 10
    Address:
      type: object
      required: [name, address1, city, state, postal_code, country]
      properties:
        name:
          type: string
          example: John Smith
        address1:
          type: string
          example: 123 Main St
        city:
          type: string
          example: Kansas City
        state:
          type: string
          minLength: 2
          maxLength: 2
          example: MO
        postal_code:
          type: string
          example: '64101'
        country:
          type: string
          enum: [US]
    CreateMailJobRequest:
      type: object
      required: [to, from, page_count]
      properties:
        to:
          $ref: '#/components/schemas/Address'
        from:
          allOf:
            - $ref: '#/components/schemas/Address'
          examples:
            - name: Acme Inc.
              address1: 500 Business Rd
              city: Dallas
              state: TX
              postal_code: '75201'
              country: US
        page_count:
          type: integer
          minimum: 1
          maximum: 10
          example: 3
        metadata:
          type: object
          additionalProperties: true
          example:
            invoice_id: '1001'
    CreateMailJobResponse:
      type: object
      required: [id, status, pages, price_cents, currency, test_mode]
      properties:
        id:
          type: string
          example: mail_123
        status:
          type: string
          enum: [queued]
        pages:
          type: integer
          example: 3
        price_cents:
          type: integer
          example: 225
        currency:
          type: string
          example: USD
        test_mode:
          type: boolean
          const: true
    MailJobStatusResponse:
      type: object
      required: [id, status, pages, price_cents, currency, test_mode, created_at, updated_at]
      properties:
        id:
          type: string
          example: mail_123
        status:
          type: string
          enum: [queued, processing, mailed, failed, cancelled]
          example: queued
        pages:
          type: integer
          example: 3
        price_cents:
          type: integer
          example: 225
        currency:
          type: string
          example: USD
        test_mode:
          type: boolean
          const: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CancelConflictResponse:
      type: object
      required: [message, current_status]
      properties:
        message:
          type: string
          example: Mail job can only be cancelled while queued.
        current_status:
          type: string
          enum: [processing, mailed, failed, cancelled]
          example: mailed
