openapi: 3.1.0
info:
  title: simiriki Public API
  version: 1.0.0
  summary: Sandbox API + AI scope extraction. Production tenant endpoints ship with the paid tier.
  description: |-
    simiriki is a Mexican operational-infrastructure company on Microsoft 365: 197 detection rules, 69 remediation playbooks, productized M365 audit, and S.O.S retainer. This spec covers the public-by-design sandbox surface plus AI scope extraction.

    All endpoints return JSON. Authentication is required only on the tenant-data endpoints (not yet in this spec). Sandbox keys can be issued without auth.

    Docs: https://simiriki.com/enterprise/api
    Console: https://simiriki.com/enterprise/api/console
  contact:
    name: simiriki
    email: hola@simiriki.com
    url: https://simiriki.com
  license:
    name: Proprietary
servers:
  - url: https://simiriki.com
    description: Production
tags:
  - name: Sandbox keys
    description: Issue, list, and revoke sandbox API keys.
  - name: Sandbox fixtures
    description: Static fixture endpoints mirroring the production response shapes.
  - name: AI
    description: AI scope extraction for intake free-text.
components:
  securitySchemes:
    bearerSandbox:
      type: http
      scheme: bearer
      bearerFormat: sk_sandbox_*
      description: Bearer token issued via POST /api/v1/sandbox/keys. Required for authenticated sandbox endpoints; example endpoints are public.
  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Machine-readable error code.
        detail:
          description: Human-readable detail or field-level error map.
      example:
        error: rate_limit_exceeded
        detail: Per-owner cap of 10 active keys reached. Revoke an existing key before issuing a new one.
    IssueKeyRequest:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
        label:
          type: string
          maxLength: 100
        expiryDays:
          type: integer
          minimum: 1
          maximum: 365
      example:
        email: developer@example.com
        label: local-dev
        expiryDays: 30
    IssueKeyResponse:
      type: object
      properties:
        ok:
          type: boolean
        key:
          type: string
          description: Cleartext key. Shown once. Save it.
        keyId:
          type: string
        expiresAt:
          type: string
          format: date-time
        docs:
          type: string
          format: uri
        note:
          type: string
      example:
        ok: true
        key: sk_sandbox_8f4c2e6a9b1d3f5e7a8c2e6a9b1d3f5e
        keyId: key_2026053118402901
        expiresAt: 2026-06-30T18:40:29Z
        docs: https://simiriki.com/enterprise/api
        note: Cleartext key shown once. Save it now; it cannot be retrieved later.
    ListKeysResponse:
      type: object
      properties:
        ok:
          type: boolean
        count:
          type: integer
        keys:
          type: array
          items:
            type: object
            properties:
              keyId:
                type: string
              prefix:
                type: string
              label:
                type: string
                nullable: true
              createdAt:
                type: string
                format: date-time
              expiresAt:
                type: string
                format: date-time
              lastUsedAt:
                type: string
                format: date-time
                nullable: true
      example:
        ok: true
        count: 2
        keys:
          - keyId: key_2026053118402901
            prefix: sk_sandbox_8f4c2e
            label: local-dev
            createdAt: 2026-05-31T18:40:29Z
            expiresAt: 2026-06-30T18:40:29Z
            lastUsedAt: 2026-06-02T14:22:11Z
          - keyId: key_2026052812150447
            prefix: sk_sandbox_a1c5d9
            label: ci-pipeline
            createdAt: 2026-05-28T12:15:04Z
            expiresAt: 2026-08-26T12:15:04Z
            lastUsedAt: null
    Finding:
      type: object
      properties:
        id:
          type: string
        rule_id:
          type: string
        title:
          type: string
        category:
          type: string
        severity:
          type: string
          enum:
            - critical
            - high
            - medium
            - low
        remediation_path:
          type: string
          enum:
            - now
            - 30d
            - 90d
            - monitor
        playbook_id:
          type: string
        description:
          type: string
        evidence:
          type: object
        affected:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
              value:
                type: string
        detected_at:
          type: string
          format: date-time
        evaluated_against:
          type: object
          properties:
            rule_version:
              type: string
            framework:
              type: array
              items:
                type: string
      example:
        id: finding_iam001_2026053118402901
        rule_id: IAM-001
        title: MFA not enforced for all admin roles
        category: Identity & Access
        severity: critical
        remediation_path: now
        playbook_id: enforce-mfa-conditional-access
        description: "One or more privileged Microsoft Entra ID roles can sign in without multi-factor authentication. Privileged accounts without MFA are the #1 critical finding in the sIPO Mexico benchmark and the primary password-spray vector."
        evidence:
          unprotected_role_count: 3
          unprotected_roles:
            - Global Administrator
            - Exchange Administrator
            - Billing Administrator
          policy_check: No Conditional Access policy targeting these roles with MFA grant control
        affected:
          - type: role
            value: Global Administrator
          - type: role
            value: Exchange Administrator
          - type: role
            value: Billing Administrator
        detected_at: 2026-05-31T18:40:29Z
        evaluated_against:
          rule_version: 2026.Q2.1
          framework:
            - CIS Microsoft 365 Benchmark v3.0 §1.1.1
            - NIST CSF 2.0 PR.AA-02
            - LFPDPPP Art. 19
    ScopeAnalysis:
      type: object
      properties:
        primaryConcerns:
          type: array
          items:
            type: string
          maxItems: 5
        systemsInPlay:
          type: array
          items:
            type: string
          maxItems: 10
        estimatedComplexity:
          type: string
          enum:
            - low
            - medium
            - high
        recommendedProduct:
          type: string
          enum:
            - audit
            - consulting
            - enterprise
            - unsure
        confidenceScore:
          type: number
          minimum: 0
          maximum: 1
        scopeMultiplier:
          type: number
          minimum: 0.7
          maximum: 1.5
        customerSummaryEs:
          type: string
          maxLength: 400
      example:
        primaryConcerns:
          - MFA enforcement gap on privileged accounts
          - External-sharing policy in SharePoint and OneDrive
          - CFDI 4.0 emission and SAT reconciliation
        systemsInPlay:
          - Microsoft 365 E3
          - Power Platform
          - Stripe
          - NetSuite ERP
          - HubSpot
        estimatedComplexity: medium
        recommendedProduct: consulting
        confidenceScore: 0.82
        scopeMultiplier: 1.1
        customerSummaryEs: "Operación mid-market (120 empleados) sobre Microsoft 365 E3 con fugas operacionales en identidad, compartición externa y emisión CFDI. Recomendado: Operational Diagnostic con énfasis en governance + automatización SAT."
    ScopePreviewRequest:
      type: object
      required:
        - freeText
      properties:
        freeText:
          type: string
          minLength: 20
          maxLength: 4000
        intakeFields:
          type: object
          properties:
            productInterest:
              type: string
            teamSize:
              type: string
            m365Status:
              type: string
            timeline:
              type: string
            budget:
              type: string
            company:
              type: string
      example:
        freeText: Somos un despacho de 120 personas en Monterrey. Usamos M365 E3 pero el setup tiene 4 años y no se ha auditado. Estamos teniendo problemas de phishing en correo, compartición de archivos sin control con clientes, y la facturación CFDI 4.0 nos toma 3 días al mes que deberían ser automatizables.
        intakeFields:
          productInterest: unsure
          teamSize: 50-200
          m365Status: yes-e3
          timeline: 1-3 months
          budget: $100K-$300K MXN
          company: Despacho Industrial Mexicano S.A. de C.V.
    ScopePreviewResponse:
      type: object
      properties:
        ok:
          type: boolean
        analysis:
          $ref: "#/components/schemas/ScopeAnalysis"
        autoActionable:
          type: boolean
        durationMs:
          type: integer
      example:
        ok: true
        analysis:
          primaryConcerns:
            - MFA enforcement gap on privileged accounts
            - External-sharing policy in SharePoint and OneDrive
            - CFDI 4.0 emission and SAT reconciliation
          systemsInPlay:
            - Microsoft 365 E3
            - Power Platform
          estimatedComplexity: medium
          recommendedProduct: consulting
          confidenceScore: 0.82
          scopeMultiplier: 1.1
          customerSummaryEs: "Operación mid-market sobre M365 E3 con fugas en identidad, compartición externa y CFDI. Recomendado: Operational Diagnostic."
        autoActionable: true
        durationMs: 1842
paths:
  /api/v1/sandbox/keys:
    post:
      tags:
        - Sandbox keys
      summary: Issue a sandbox API key
      description: Public-by-design — anyone can request a sandbox key for their own email. Per-owner limit of 10 active keys. The cleartext key is returned once; save it.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IssueKeyRequest"
      responses:
        "201":
          description: Key issued. Cleartext shown once.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IssueKeyResponse"
        "400":
          description: Invalid request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit or per-owner cap exceeded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    get:
      tags:
        - Sandbox keys
      summary: List active sandbox keys for an email
      parameters:
        - name: email
          in: query
          required: true
          schema:
            type: string
            format: email
      responses:
        "200":
          description: List of active keys (metadata only — cleartext is never returned after issuance).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListKeysResponse"
        "400":
          description: Missing email.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/sandbox/keys/{keyId}:
    delete:
      tags:
        - Sandbox keys
      summary: Revoke a sandbox key
      parameters:
        - name: keyId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Key revoked.
        "404":
          description: Key not found.
  /api/v1/sandbox/example/scan:
    get:
      tags:
        - Sandbox fixtures
      summary: Example scan response
      description: Returns a fixture scan response. No auth, no tenant required.
      responses:
        "200":
          description: Example scan payload.
          content:
            application/json:
              schema:
                type: object
  /api/v1/sandbox/example/webhook:
    get:
      tags:
        - Sandbox fixtures
      summary: Example webhook payload
      parameters:
        - name: event
          in: query
          required: false
          schema:
            type: string
            enum:
              - scan.completed
              - audit.delivered
              - subscription.activated
              - finding.detected
            default: scan.completed
      responses:
        "200":
          description: Example webhook payload for the requested event type.
          content:
            application/json:
              schema:
                type: object
  /api/v1/sandbox/example/finding:
    get:
      tags:
        - Sandbox fixtures
      summary: Example finding response
      parameters:
        - name: id
          in: query
          required: false
          schema:
            type: string
            enum:
              - IAM-001
              - IAM-008
              - EML-001
              - EML-002
              - EML-003
              - DLP-003
              - AUD-001
            default: IAM-001
      responses:
        "200":
          description: Example finding for the requested rule_id.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Finding"
  /api/v1/sandbox/example/playbook:
    get:
      tags:
        - Sandbox fixtures
      summary: Example playbook execution response
      parameters:
        - name: id
          in: query
          required: false
          schema:
            type: string
            enum:
              - enforce-mfa-conditional-access
              - block-legacy-auth
              - configure-spf-dkim-dmarc
              - restrict-external-sharing
              - enable-unified-audit-log
            default: enforce-mfa-conditional-access
      responses:
        "200":
          description: Example playbook payload.
          content:
            application/json:
              schema:
                type: object
  /api/v1/sandbox/example/metrics:
    get:
      tags:
        - Sandbox fixtures
      summary: Example posture metrics
      description: sIPO (simiriki Infrastructure Posture Observation) and sIRR (simiriki Infrastructure Risk Ratio) over time, plus category breakdown and industry benchmark.
      responses:
        "200":
          description: Example metrics payload.
          content:
            application/json:
              schema:
                type: object
  /api/scope/preview:
    post:
      tags:
        - AI
      summary: Preview AI scope extraction on intake free-text
      description: Runs the LLM scope extractor (Anthropic Claude Haiku 4.5) against intake free-text and returns a structured ScopeAnalysis. Rate-limited to 3 calls/60s/IP. The internal `reasoning` field is stripped from the response.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ScopePreviewRequest"
      responses:
        "200":
          description: Scope analysis returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScopePreviewResponse"
        "400":
          description: Invalid request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Extractor unavailable (e.g. ANTHROPIC_API_KEY missing).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
