openapi: 3.0.3
info:
  title: TinyTax Formation Agent API
  version: '1.0'
  description: |
    REST API for formation agents to submit dormant company filings to HMRC (CT600) and Companies House (accounts).

    ## Overview

    The TinyTax API enables you to automate dormant company filings at scale. Submit CT600 nil returns to HMRC and dormant accounts to Companies House with minimal required fields - we auto-derive company details from Companies House.

    ## Authentication

    All API requests require an API key passed in the `X-API-Key` header:

    ```
    X-API-Key: tt_live_k1a2b3c4d5e6_s9x8y7z6w5v4u3t2r1q0p9o8n7m6l5k4
    ```

    Get your API key from the [API Dashboard](/profile/api).

    ## Idempotency

    All POST requests require an `Idempotency-Key` header to prevent duplicate filings:

    ```
    Idempotency-Key: your-unique-request-id
    ```

    If you retry a request with the same idempotency key and request body, you'll receive the original response. Keys expire after 24 hours.

    ## Rate Limits

    | Limit | Default |
    |-------|---------|
    | Per minute | 60 requests |
    | Per day | 1,000 requests |

    Rate limit headers are included in all responses:
    - `X-RateLimit-Limit-Minute`
    - `X-RateLimit-Remaining-Minute`
    - `X-RateLimit-Limit-Day`
    - `X-RateLimit-Remaining-Day`

    ## Smart Defaults

    The API automatically derives most data from Companies House:
    - Company name and entity type
    - Period start date (from ARD or incorporation)
    - First active director name
    - Share capital (from IN01, defaults to £100)
    - Prior year figures (from filing history)

    This means a dormant accounts filing requires just **3 fields**: `company_number`, `period_end`, and `credentials`.

  contact:
    name: TinyTax Support
    email: support@tinytax.co.uk
    url: https://tinytax.co.uk/support
  x-logo:
    url: /assets/brand/logos/logo_primary.png
    altText: TinyTax

servers:
  - url: https://tinytax.co.uk/api/v1
    description: Production
  - url: https://staging.tinytax.co.uk/api/v1
    description: Staging (HMRC/CH test environments)

tags:
  - name: Filings
    description: Submit and manage filings
  - name: Documents
    description: Download generated documents
  - name: Health
    description: API health check

security:
  - ApiKeyAuth: []

paths:
  /health:
    get:
      operationId: getHealth
      summary: Health check
      description: Returns API status. No authentication required.
      tags:
        - Health
      security: []
      responses:
        '200':
          description: API is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  version:
                    type: string
                    example: '1.0'
                  timestamp:
                    type: string
                    format: date-time

  /filings:
    get:
      operationId: listFilings
      summary: List filings
      description: Returns a paginated list of your filings, ordered by creation date (newest first).
      tags:
        - Filings
      parameters:
        - name: status
          in: query
          description: Filter by status
          schema:
            type: string
            enum: [pending, submitted, accepted, rejected, error]
        - name: type
          in: query
          description: Filter by filing type
          schema:
            type: string
            enum: [ct600, accounts]
        - name: company_number
          in: query
          description: Filter by company number
          schema:
            type: string
        - name: limit
          in: query
          description: Number of results per page (max 100)
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: offset
          in: query
          description: Number of results to skip
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of filings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FilingsList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'

  /filings/ct600:
    post:
      operationId: submitCT600
      summary: Submit dormant CT600
      description: |
        Submit a dormant CT600 nil return to HMRC.

        **Minimal request:** Only `company_number`, `utr`, `period_end`, and `credentials` are required. All other fields are auto-derived from Companies House.

        **Extended periods:** Accounting periods over 12 months are automatically split into two CT600 submissions (first 12 months, then remainder). You'll receive a single `filing_id` with `period_split: true`.

        **Async processing:** Returns immediately with status `submitted`. Poll `GET /filings/{id}` for final status.
      tags:
        - Filings
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CT600Request'
            examples:
              minimal:
                summary: Minimal request (recommended)
                value:
                  company_number: '12345678'
                  utr: '1234567890'
                  period_end: '2024-12-31'
                  credentials:
                    government_gateway_id: '123456789012'
                    government_gateway_password: 'password123'
              with_overrides:
                summary: With optional overrides
                value:
                  company_number: '12345678'
                  utr: '1234567890'
                  period_end: '2024-12-31'
                  company:
                    name: 'Override Name Ltd'
                  period:
                    start: '2024-01-01'
                  declarant:
                    name: 'Jane Smith'
                    status: 'Company Secretary'
                  credentials:
                    government_gateway_id: '123456789012'
                    government_gateway_password: 'password123'
      responses:
        '202':
          description: Filing submitted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CT600Response'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientBalance'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/GatewayError'

  /filings/accounts:
    post:
      operationId: submitAccounts
      summary: Submit dormant accounts
      description: |
        Submit dormant accounts to Companies House.

        **Minimal request:** Only `company_number`, `period_end`, and `credentials` are required. All other fields are auto-derived from Companies House.

        **Async processing:** Returns immediately with status `submitted`. Poll `GET /filings/{id}` for final status.
      tags:
        - Filings
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccountsRequest'
            examples:
              minimal:
                summary: Minimal request (recommended)
                value:
                  company_number: '12345678'
                  period_end: '2024-12-31'
                  credentials:
                    company_auth_code: 'ABC123'
              with_overrides:
                summary: With optional overrides
                value:
                  company_number: '12345678'
                  period_end: '2024-12-31'
                  company:
                    name: 'Override Name Ltd'
                    entity_type: 'private-limited-guarant-nsc'
                  period:
                    start: '2024-01-01'
                    is_first_year: true
                  approval:
                    date: '2025-01-15'
                    director_name: 'Jane Smith'
                    director_position: 'Director'
                  balance_sheet:
                    share_capital: 1
                    cash: 1
                    creditors: 0
                    profit_loss_account: 0
                  credentials:
                    company_auth_code: 'ABC123'
      responses:
        '202':
          description: Filing submitted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountsResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientBalance'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          $ref: '#/components/responses/GatewayError'

  /filings/{filing_id}:
    get:
      operationId: getFiling
      summary: Get filing status
      description: |
        Returns the current status of a filing.

        **Polling:** After submission, poll this endpoint to check for final status. Recommended interval: every 30 seconds for first 5 minutes, then every 5 minutes.

        **Split periods:** For extended period CT600s, the response includes a `parts` array with individual HMRC submission statuses.
      tags:
        - Filings
      parameters:
        - name: filing_id
          in: path
          required: true
          description: Filing ID (e.g., `fil_abc123def456`)
          schema:
            type: string
      responses:
        '200':
          description: Filing details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FilingStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/FilingNotFound'

  /filings/{filing_id}/documents:
    get:
      operationId: listDocuments
      summary: List available documents
      description: |
        Returns a list of documents available for download.

        Documents are only available after the filing reaches `accepted` status.
      tags:
        - Documents
      parameters:
        - name: filing_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: List of documents
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsList'
        '400':
          description: Filing not complete
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                error:
                  code: FILING_NOT_COMPLETE
                  message: Filing not yet accepted, documents not generated
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/FilingNotFound'

  /filings/{filing_id}/documents/{type}:
    get:
      operationId: downloadDocument
      summary: Download document
      description: |
        Downloads a specific document.

        **Document types:**
        | Type | Description | Available For |
        |------|-------------|---------------|
        | `ct600_pdf` | CT600 form as PDF | CT600 filings |
        | `ct600_xml` | Raw HMRC submission XML | CT600 filings |
        | `ixbrl_accounts` | iXBRL accounts document | Accounts filings |
        | `accounts_pdf` | Human-readable accounts PDF | Accounts filings |
      tags:
        - Documents
      parameters:
        - name: filing_id
          in: path
          required: true
          schema:
            type: string
        - name: type
          in: path
          required: true
          schema:
            type: string
            enum: [ct600_pdf, ct600_xml, ixbrl_accounts, accounts_pdf]
      responses:
        '200':
          description: Document file
          content:
            application/pdf:
              schema:
                type: string
                format: binary
            application/xml:
              schema:
                type: string
                format: binary
            application/xhtml+xml:
              schema:
                type: string
                format: binary
        '400':
          description: Filing not complete
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                error:
                  code: DOCUMENT_NOT_FOUND
                  message: Document type not available for this filing

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        API key in format: `tt_{env}_{key_id}_{secret}`

        Example: `tt_live_k1a2b3c4d5e6_s9x8y7z6w5v4u3t2r1q0p9o8n7m6l5k4`

  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Unique key for request deduplication (max 64 characters)
      schema:
        type: string
        maxLength: 64
      example: 'req_2025-01-15_12345678_ct600'

  schemas:
    CT600Request:
      type: object
      required:
        - company_number
        - utr
        - period_end
        - credentials
      properties:
        company_number:
          type: string
          pattern: '^[A-Z0-9]{8}$'
          description: 8-character company registration number
          example: '12345678'
        utr:
          type: string
          pattern: '^\d{10}$'
          description: 10-digit Unique Taxpayer Reference
          example: '1234567890'
        period_end:
          type: string
          format: date
          description: Accounting period end date (YYYY-MM-DD)
          example: '2024-12-31'
        company:
          type: object
          description: Override company details (auto-derived from CH if not provided)
          properties:
            name:
              type: string
              description: Company name
        period:
          type: object
          description: Override period details (auto-derived if not provided)
          properties:
            start:
              type: string
              format: date
              description: Period start date
        declarant:
          type: object
          description: Override declarant details (first director used if not provided)
          properties:
            name:
              type: string
              description: Name of person signing declaration
            status:
              type: string
              enum: [Director, Company Secretary, Agent]
              description: Position of declarant
        credentials:
          type: object
          required:
            - government_gateway_id
            - government_gateway_password
          properties:
            government_gateway_id:
              type: string
              description: Government Gateway user ID
              example: '123456789012'
            government_gateway_password:
              type: string
              format: password
              description: Government Gateway password
              example: 'password123'

    AccountsRequest:
      type: object
      required:
        - company_number
        - period_end
        - credentials
      properties:
        company_number:
          type: string
          pattern: '^[A-Z0-9]{8}$'
          description: 8-character company registration number
          example: '12345678'
        period_end:
          type: string
          format: date
          description: Accounting period end date (YYYY-MM-DD)
          example: '2024-12-31'
        company:
          type: object
          description: Override company details
          properties:
            name:
              type: string
            entity_type:
              type: string
              description: Companies House entity type code
        period:
          type: object
          properties:
            start:
              type: string
              format: date
            is_first_year:
              type: boolean
              description: Whether this is the first accounting period
        approval:
          type: object
          description: Override approval details
          properties:
            date:
              type: string
              format: date
              description: Date accounts were approved
            director_name:
              type: string
            director_position:
              type: string
        balance_sheet:
          type: object
          description: Override balance sheet values (must balance if provided)
          properties:
            share_capital:
              type: number
            cash:
              type: number
            creditors:
              type: number
            profit_loss_account:
              type: number
        credentials:
          type: object
          required:
            - company_auth_code
          properties:
            company_auth_code:
              type: string
              pattern: '^[A-Z0-9]{6}$'
              description: 6-character company authentication code
              example: 'ABC123'

    CT600Response:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          properties:
            filing_id:
              type: string
              example: 'fil_abc123def456'
            status:
              type: string
              enum: [pending, submitted, accepted, rejected, error]
              example: submitted
            hmrc_correlation_id:
              type: string
              example: 'A1B2C3D4E5F6'
            period_split:
              type: boolean
              description: True if period was split into multiple submissions
              example: false
            parts:
              type: array
              description: Only present if period_split is true
              items:
                $ref: '#/components/schemas/FilingPart'
            submitted_at:
              type: string
              format: date-time
            derived_values:
              type: object
              description: Values auto-derived from Companies House
              properties:
                company_name:
                  type: string
                period_start:
                  type: string
                  format: date
                is_first_year:
                  type: boolean
                declarant_name:
                  type: string
                declarant_status:
                  type: string
        request_id:
          type: string
          format: uuid

    AccountsResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          properties:
            filing_id:
              type: string
              example: 'fil_xyz789abc012'
            status:
              type: string
              enum: [pending, submitted, accepted, rejected, error]
              example: submitted
            ch_submission_number:
              type: string
              example: 'CH-2025-001234'
            submitted_at:
              type: string
              format: date-time
            derived_values:
              type: object
              properties:
                company_name:
                  type: string
                entity_type:
                  type: string
                period_start:
                  type: string
                  format: date
                is_first_year:
                  type: boolean
                director_name:
                  type: string
                approval_date:
                  type: string
                  format: date
                share_capital:
                  type: number
                cash:
                  type: number
        request_id:
          type: string
          format: uuid

    FilingStatus:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: object
          properties:
            filing_id:
              type: string
            filing_type:
              type: string
              enum: [ct600, accounts]
            company_number:
              type: string
            company_name:
              type: string
            period:
              type: object
              properties:
                start:
                  type: string
                  format: date
                end:
                  type: string
                  format: date
            status:
              type: string
              enum: [pending, submitted, accepted, rejected, error]
            period_split:
              type: boolean
            parts:
              type: array
              items:
                $ref: '#/components/schemas/FilingPart'
            hmrc_status:
              type: string
              description: For CT600 filings (single period)
            hmrc_correlation_id:
              type: string
            hmrc_iru_number:
              type: string
              description: IRmark unique reference (on acceptance)
            ch_status:
              type: string
              description: For accounts filings
            ch_submission_number:
              type: string
            error:
              type: object
              description: Present if status is rejected or error
              properties:
                code:
                  type: string
                message:
                  type: string
            created_at:
              type: string
              format: date-time
            updated_at:
              type: string
              format: date-time

    FilingPart:
      type: object
      properties:
        part_number:
          type: integer
          example: 1
        period:
          type: object
          properties:
            start:
              type: string
              format: date
            end:
              type: string
              format: date
        hmrc_status:
          type: string
          enum: [pending, submitted, polling, accepted, rejected, error]
        hmrc_correlation_id:
          type: string
        hmrc_iru_number:
          type: string

    FilingsList:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: object
          properties:
            filings:
              type: array
              items:
                $ref: '#/components/schemas/FilingSummary'
            pagination:
              type: object
              properties:
                total:
                  type: integer
                limit:
                  type: integer
                offset:
                  type: integer
                has_more:
                  type: boolean

    FilingSummary:
      type: object
      properties:
        filing_id:
          type: string
        filing_type:
          type: string
          enum: [ct600, accounts]
        company_number:
          type: string
        company_name:
          type: string
        period_end:
          type: string
          format: date
        status:
          type: string
        created_at:
          type: string
          format: date-time

    DocumentsList:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: object
          properties:
            filing_id:
              type: string
            documents:
              type: array
              items:
                type: object
                properties:
                  type:
                    type: string
                    enum: [ct600_pdf, ct600_xml, ixbrl_accounts, accounts_pdf]
                  name:
                    type: string
                  size_bytes:
                    type: integer
                  generated_at:
                    type: string
                    format: date-time

    Error:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                  message:
                    type: string
        request_id:
          type: string
          format: uuid

  responses:
    Unauthorized:
      description: Authentication failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing_key:
              summary: Missing API key
              value:
                success: false
                error:
                  code: AUTH_REQUIRED
                  message: Missing X-API-Key header
            invalid_key:
              summary: Invalid API key
              value:
                success: false
                error:
                  code: INVALID_KEY
                  message: Invalid or expired API key

    ValidationError:
      description: Request validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error:
              code: VALIDATION_ERROR
              message: Request validation failed
              details:
                - field: utr
                  message: UTR must be exactly 10 digits
            request_id: '550e8400-e29b-41d4-a716-446655440000'

    InsufficientBalance:
      description: Insufficient account balance
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error:
              code: INSUFFICIENT_BALANCE
              message: Insufficient balance. Please top up your account.
              details:
                - field: balance
                  message: 'Required: £20.00, Available: £5.00'
            request_id: '550e8400-e29b-41d4-a716-446655440000'

    IdempotencyConflict:
      description: Idempotency key conflict
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error:
              code: IDEMPOTENCY_KEY_CONFLICT
              message: This idempotency key was used with a different request body
            request_id: '550e8400-e29b-41d4-a716-446655440000'

    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait before retrying
        X-RateLimit-Remaining-Minute:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error:
              code: RATE_LIMIT_EXCEEDED
              message: Too many requests. Please wait before retrying.
            request_id: '550e8400-e29b-41d4-a716-446655440000'

    FilingNotFound:
      description: Filing not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error:
              code: FILING_NOT_FOUND
              message: Filing not found or not owned by this API key

    GatewayError:
      description: External service error
      headers:
        Retry-After:
          schema:
            type: integer
          description: Suggested retry delay in seconds
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            hmrc_unavailable:
              summary: HMRC unavailable
              value:
                success: false
                error:
                  code: HMRC_UNAVAILABLE
                  message: HMRC service temporarily unavailable
                request_id: '550e8400-e29b-41d4-a716-446655440000'
            ch_unavailable:
              summary: Companies House unavailable
              value:
                success: false
                error:
                  code: CH_UNAVAILABLE
                  message: Companies House service temporarily unavailable
                request_id: '550e8400-e29b-41d4-a716-446655440000'
            hmrc_auth_failed:
              summary: HMRC credentials invalid
              value:
                success: false
                error:
                  code: HMRC_AUTH_FAILED
                  message: Government Gateway credentials invalid
                request_id: '550e8400-e29b-41d4-a716-446655440000'
