openapi: '3.1.0'
info:
  title: Senserity API
  version: '2026-04-20'
  description: |
    Risk intelligence API for UK company due diligence. Senserity aggregates data from 25+ public sources
    (Companies House, sanctions lists, HSE, Environment Agency, ICO, and more) and runs 665+ automated
    insight tests across 9 risk categories to produce risk scores, letter grades, and actionable findings.

    ## Authentication

    All endpoints (except `/status`) require a Bearer token in the Authorization header:

    ```
    Authorization: Bearer sns_live_your_key_here
    ```

    Create API keys in **Settings > API** on your Senserity dashboard. Requires a Professional or Enterprise subscription.

    ## Rate Limits

    Per-key rate limits are enforced. Every response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`,
    and `X-RateLimit-Reset` headers.

    | Tier | Requests/minute | Requests/day |
    |------|----------------|-------------|
    | Professional | 20 | 2,000 |
    | Enterprise | 60 | 10,000 |

    ## Pagination

    All list endpoints use cursor-based pagination. Pass `cursor` from the previous response's `meta.cursor`
    to fetch the next page. The `limit` parameter controls page size (1-100, default 20).

    ## MCP Server

    An MCP (Model Context Protocol) server is available for AI agent integration. See the
    [MCP Server documentation](/docs/mcp) for setup instructions.
  contact:
    name: Senserity Support
    email: support@senserity.co.uk
    url: https://senserity.co.uk
  license:
    name: Proprietary

servers:
  - url: https://app.senserity.co.uk/api/v1
    description: Production

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: 'Senserity API key. Format: sns_live_<body>_<checksum>'

  schemas:
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, request_id]
          properties:
            code:
              type: string
              description: Machine-readable error code
              example: validation_error
            message:
              type: string
              description: Human-readable error message with actionable fix suggestion
              example: "The 'company_number' field must be an 8-digit Companies House number (e.g., '00006400'). Received: 'ACME Corp'"
            request_id:
              type: string
              format: uuid
              description: Unique request ID for support investigations
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                  issue:
                    type: string
                  example:
                    type: string

    PaginationMeta:
      type: object
      properties:
        cursor:
          type: string
          nullable: true
          description: Opaque cursor for the next page. Pass as the `cursor` query parameter.
        has_more:
          type: boolean
          description: Whether more pages exist
        total:
          type: integer
          description: Approximate total count of results

    CompanySummary:
      type: object
      properties:
        company_number:
          type: string
          example: '00006400'
        company_name:
          type: string
          nullable: true
          example: ROLLS-ROYCE HOLDINGS PLC
        status:
          type: string
          nullable: true
          example: Active
        company_category:
          type: string
          nullable: true
        sic_codes:
          type: array
          items:
            type: string
        monitoring_enabled:
          type: boolean
        monitoring_frequency:
          type: integer
          description: Days between automated checks
        label:
          type: string
          nullable: true
        added_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true

    RiskSummary:
      type: object
      description: Overall risk profile for a company. The primary response for quick risk assessment.
      properties:
        company_number:
          type: string
        company_name:
          type: string
          nullable: true
        overall_risk_score:
          type: number
          nullable: true
          description: 'Overall risk score (0-100). Lower is better. 0 = minimal risk.'
        letter_grade:
          type: string
          nullable: true
          description: 'Risk grade: A (lowest risk) through E (highest risk)'
          enum: [A, B, C, D, E]
        red_flags:
          type: integer
          description: Count of critical/high-severity test failures
        red_flag_tests:
          type: array
          items:
            type: object
            properties:
              test_id:
                type: string
              test_name:
                type: string
              category:
                type: string
              severity:
                type: string
              headline:
                type: string
        category_scores:
          type: object
          description: Risk scores per category with weights
          additionalProperties:
            type: object
            properties:
              score:
                type: number
              weight:
                type: number
        executive_summary:
          type: string
          nullable: true
          description: AI-generated narrative summary of the company's risk profile
        trajectory:
          type: string
          nullable: true
          enum: [improving, stable, deteriorating]
        last_run_at:
          type: string
          format: date-time
          nullable: true

    AlertItem:
      type: object
      properties:
        id:
          type: integer
        company_number:
          type: string
        company_name:
          type: string
          nullable: true
        test_id:
          type: string
        test_name:
          type: string
        severity:
          type: string
          enum: [critical, high, medium, low, info]
        category:
          type: string
        status:
          type: string
        headline:
          type: string
        acknowledged:
          type: boolean
        created_at:
          type: string
          format: date-time

  parameters:
    CursorParam:
      name: cursor
      in: query
      schema:
        type: string
      description: Pagination cursor from previous response
    LimitParam:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
      description: Results per page (1-100)
    CompanyNumberPath:
      name: company_number
      in: path
      required: true
      schema:
        type: string
        pattern: '^\d{8}$'
      description: 8-digit Companies House number (e.g., "00006400")

paths:
  /status:
    get:
      operationId: getStatus
      summary: API status and health check
      description: Returns API health, version, and optionally tenant info if a valid Bearer token is provided. No authentication required.
      security: []
      responses:
        '200':
          description: API is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      status:
                        type: string
                        example: ok
                      version:
                        type: string
                        example: '2026-04-20'
                      documentation:
                        type: string

  /me:
    get:
      operationId: getMe
      summary: Current tenant and API key info
      description: Returns tenant context, subscription tier, credit balance, rate limit status, and current API key details. Use this to verify connectivity and inspect key capabilities.
      responses:
        '200':
          description: Tenant context
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      tenant_id:
                        type: string
                      tenant_name:
                        type: string
                      subscription_tier:
                        type: string
                      credits_remaining:
                        type: integer
                      credits_allocated:
                        type: integer

  /companies:
    get:
      operationId: listCompanies
      summary: List watchlist companies
      description: Paginated list of companies on the tenant's watchlist. Supports search by name or number, and filtering by label and monitoring status. Use `?include=latest_scores` to include risk scores.
      parameters:
        - $ref: '#/components/parameters/CursorParam'
        - $ref: '#/components/parameters/LimitParam'
        - name: search
          in: query
          schema:
            type: string
          description: Search by company name or number
        - name: label
          in: query
          schema:
            type: string
          description: Filter by watchlist label
        - name: monitoring
          in: query
          schema:
            type: string
            enum: ['true', 'false']
          description: Filter by monitoring status
        - name: include
          in: query
          schema:
            type: string
          description: 'Comma-separated includes. Supported: latest_scores'
      responses:
        '200':
          description: Paginated company list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/CompanySummary'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'

    post:
      operationId: addCompany
      summary: Add company to watchlist
      description: Add a UK company to the monitoring watchlist by Companies House number. Optionally set a label, monitoring frequency, and whether to start monitoring immediately.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [company_number]
              properties:
                company_number:
                  type: string
                  description: 8-digit Companies House number
                  example: '00006400'
                label:
                  type: string
                  nullable: true
                  description: Watchlist label
                monitoring_enabled:
                  type: boolean
                  default: true
                monitoring_frequency:
                  type: integer
                  enum: [1, 7, 14, 30]
                  default: 30
                  description: Days between automated checks
      responses:
        '201':
          description: Company added
        '409':
          description: Company already on watchlist

  /companies/{company_number}:
    parameters:
      - $ref: '#/components/parameters/CompanyNumberPath'
    get:
      operationId: getCompany
      summary: Get company profile
      description: Full company profile merging watchlist and Companies House data. Includes registered address, SIC codes, incorporation date, mortgage data, monitoring status, and more.
      responses:
        '200':
          description: Company profile
        '404':
          description: Company not on watchlist

    patch:
      operationId: updateCompany
      summary: Update watchlist settings
      description: 'Update monitoring status, frequency, label, or notes for a company. Use monitoring_enabled: false to pause monitoring without removing the company.'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                monitoring_enabled:
                  type: boolean
                  description: 'true to enable monitoring, false to pause'
                monitoring_frequency:
                  type: integer
                  enum: [1, 7, 14, 30]
                label:
                  type: string
                  nullable: true
                notes:
                  type: string
                  nullable: true
      responses:
        '200':
          description: Updated company

    delete:
      operationId: removeCompany
      summary: Remove from watchlist
      description: Remove a company from the watchlist. Historical data (insight results, reports) is retained but no longer updated.
      responses:
        '204':
          description: Company removed

  /companies/{company_number}/officers:
    parameters:
      - $ref: '#/components/parameters/CompanyNumberPath'
    get:
      operationId: listOfficers
      summary: List company officers
      description: Current and resigned officers including name, role, appointment/resignation dates, nationality, and occupation.
      parameters:
        - $ref: '#/components/parameters/CursorParam'
        - $ref: '#/components/parameters/LimitParam'
        - name: status
          in: query
          schema:
            type: string
            enum: [active, resigned, all]
            default: active
      responses:
        '200':
          description: Officer list

  /companies/{company_number}/filings:
    parameters:
      - $ref: '#/components/parameters/CompanyNumberPath'
    get:
      operationId: listFilings
      summary: List financial history
      description: Filing history with financial data including turnover, profit/loss, assets, liabilities, and employee count.
      parameters:
        - $ref: '#/components/parameters/CursorParam'
        - $ref: '#/components/parameters/LimitParam'
      responses:
        '200':
          description: Filing history

  /companies/{company_number}/insights:
    parameters:
      - $ref: '#/components/parameters/CompanyNumberPath'
    get:
      operationId: listInsights
      summary: List all insight results
      description: All latest insight test results grouped by risk category. Each test includes status, score, headline, evidence, and display data. Filterable by category and severity.
      parameters:
        - name: category
          in: query
          schema:
            type: string
          description: 'Filter by category (comma-separated): financial, governance, compliance, cyber, legal, media, operational, social, network'
        - name: severity
          in: query
          schema:
            type: string
          description: 'Filter by severity (comma-separated): critical, high, medium, low, info'
      responses:
        '200':
          description: Insight results grouped by category

  /companies/{company_number}/insights/summary:
    parameters:
      - $ref: '#/components/parameters/CompanyNumberPath'
    get:
      operationId: getRiskSummary
      summary: Get risk summary
      description: 'Overall risk score, letter grade, category breakdown, red flags, executive summary, and trajectory. This is the primary endpoint for assessing a company — it provides a complete risk picture in a single call.'
      responses:
        '200':
          description: Risk summary
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/RiskSummary'

  /companies/{company_number}/insights/{test_id}:
    parameters:
      - $ref: '#/components/parameters/CompanyNumberPath'
      - name: test_id
        in: path
        required: true
        schema:
          type: string
        description: 'Test code (e.g., "FIN-001") or numeric test ID'
    get:
      operationId: getInsightResult
      summary: Get single insight result
      description: Full detail for a single insight test including display data, evidence, external links, and confidence score.
      responses:
        '200':
          description: Test result detail

  /companies/{company_number}/insights/run:
    parameters:
      - $ref: '#/components/parameters/CompanyNumberPath'
    post:
      operationId: runInsights
      summary: Trigger insight assessment
      description: 'Trigger a fresh risk assessment. Runs asynchronously — use the returned run_id with GET /runs/{run_id} to poll for completion. Consumes credits. Supports the Idempotency-Key header.'
      parameters:
        - name: Idempotency-Key
          in: header
          schema:
            type: string
          description: Unique key to prevent duplicate runs from retried requests
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                categories:
                  type: array
                  items:
                    type: string
                  description: 'Optional: only assess specific categories'
      responses:
        '202':
          description: Run queued

  /runs/{run_id}:
    parameters:
      - name: run_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      operationId: pollRun
      summary: Poll run status
      description: 'Check the status of an insight run. Status progresses: queued → running → complete/failed. Once complete, includes test counts and links to results.'
      responses:
        '200':
          description: Run status

  /reports:
    get:
      operationId: listReports
      summary: List reports
      description: Paginated list of PDF due diligence reports. Filterable by company and report type.
      parameters:
        - $ref: '#/components/parameters/CursorParam'
        - $ref: '#/components/parameters/LimitParam'
        - name: company_number
          in: query
          schema:
            type: string
        - name: report_type
          in: query
          schema:
            type: string
            enum: [supplier, customer, acquisition, competitor, partner_subsidiary, self_assessment]
      responses:
        '200':
          description: Report list

    post:
      operationId: generateReport
      summary: Generate a report
      description: Generate a new PDF due diligence report. Consumes 2 credits. Report generation is asynchronous. Supports the Idempotency-Key header.
      parameters:
        - name: Idempotency-Key
          in: header
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [company_number]
              properties:
                company_number:
                  type: string
                report_type:
                  type: string
                  enum: [supplier, customer, acquisition, competitor, partner_subsidiary, self_assessment]
                  default: supplier
      responses:
        '202':
          description: Report queued

  /reports/{report_id}:
    parameters:
      - name: report_id
        in: path
        required: true
        schema:
          type: string
    get:
      operationId: getReport
      summary: Get report metadata
      description: Report details including status, risk score, AI summary, and download URL when ready.
      responses:
        '200':
          description: Report metadata

  /reports/{report_id}/download:
    parameters:
      - name: report_id
        in: path
        required: true
        schema:
          type: string
    get:
      operationId: downloadReport
      summary: Download report PDF
      description: Stream the PDF report binary. Returns Content-Type application/pdf with a descriptive filename.
      responses:
        '200':
          description: PDF binary
          content:
            application/pdf:
              schema:
                type: string
                format: binary

  /alerts:
    get:
      operationId: listAlerts
      summary: List risk alerts
      description: Risk alerts generated when insight tests detect new issues or status changes. Filterable by company, severity, category, date, and acknowledgement status.
      parameters:
        - $ref: '#/components/parameters/CursorParam'
        - $ref: '#/components/parameters/LimitParam'
        - name: company_number
          in: query
          schema:
            type: string
          description: Filter alerts for a specific company
        - name: severity
          in: query
          schema:
            type: string
          description: 'Comma-separated: critical, high, medium, low, info'
        - name: category
          in: query
          schema:
            type: string
          description: 'Comma-separated: financial, governance, compliance, etc.'
        - name: since
          in: query
          schema:
            type: string
            format: date-time
          description: Only alerts after this timestamp
        - name: acknowledged
          in: query
          schema:
            type: string
            enum: ['true', 'false']
      responses:
        '200':
          description: Alert list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AlertItem'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
