openapi: 3.1.0
info:
  title: Blit Pro API
  version: "1.0.0"
  summary: Subscriber Pro API (Bearer API keys)
  description: |
    Programmatic read access (plus light watchlist CRUD) for Blit Intelligence Desk Pro subscribers.

    **Base path:** `/api/v1/key`

    Create keys in the Pro desk under **Settings → Integrations**. The full secret (`bt_…`) is shown once.

    **Not available via API key (configure in Settings):** alert-rule CRUD, webhook endpoint CRUD/test/rotate, web push subscribe.
    Outbound webhooks still deliver `alert.triggered` to URLs you configure in the desk (HMAC-signed).

    **Public (no auth) teaser API:** `/api/v1/public/*` — GRI snapshot, feed teasers, events, gadget, market quotes.
    OpenAPI for this Pro surface: `GET /api/v1/openapi.yaml` · `GET /api/v1/openapi.json`
  contact:
    name: Blit Intelligence Desk
servers:
  - url: https://core.intelligence-desk.com
    description: Production
  - url: http://localhost
    description: Local
tags:
  - name: Risk
  - name: Feed
  - name: Search
  - name: Market
  - name: Watchlist
  - name: Account
  - name: Meta
security:
  - bearerApiKey: []
paths:
  /api/v1/openapi.yaml:
    get:
      tags: [Meta]
      summary: OpenAPI schema (YAML)
      security: []
      responses:
        "200":
          description: OpenAPI 3.1 document
          content:
            application/yaml:
              schema:
                type: string
  /api/v1/openapi.json:
    get:
      tags: [Meta]
      summary: OpenAPI schema (JSON)
      security: []
      responses:
        "200":
          description: OpenAPI 3.1 document
  /api/v1/key/risk:
    get:
      tags: [Risk]
      summary: Current Global Risk Index
      responses:
        "200":
          description: Current GRI snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RiskSnapshot"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "429":
          $ref: "#/components/responses/TooManyRequests"
  /api/v1/key/risk/components:
    get:
      tags: [Risk]
      summary: GRI component scores
      responses:
        "200":
          description: Component breakdown
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
  /api/v1/key/risk/history:
    get:
      tags: [Risk]
      summary: GRI history
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 24
        - name: events
          in: query
          schema:
            type: integer
            enum: [0, 1]
            description: Include related events when 1
      responses:
        "200":
          description: Historical points
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
  /api/v1/key/risk/drivers:
    get:
      tags: [Risk]
      summary: Risk drivers for a window
      parameters:
        - name: hours
          in: query
          schema:
            type: integer
            default: 4
      responses:
        "200":
          description: Drivers and delta for the window
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
  /api/v1/key/risk/delta:
    get:
      tags: [Risk]
      summary: Multi-window GRI deltas
      responses:
        "200":
          description: Deltas for 1h / 4h / 24h
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
  /api/v1/key/feed:
    get:
      tags: [Feed]
      summary: List live briefings
      parameters:
        - name: locale
          in: query
          schema:
            type: string
            enum: [en, pt, es]
        - name: page
          in: query
          schema:
            type: integer
      responses:
        "200":
          description: Paginated briefings
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
  /api/v1/key/feed/{idOrSlug}:
    get:
      tags: [Feed]
      summary: Show one briefing
      parameters:
        - name: idOrSlug
          in: path
          required: true
          schema:
            type: string
        - name: locale
          in: query
          schema:
            type: string
            enum: [en, pt, es]
      responses:
        "200":
          description: Full briefing dossier
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "404":
          description: Not found
  /api/v1/key/search:
    get:
      tags: [Search]
      summary: Search events and briefings
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Search hits
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "422":
          $ref: "#/components/responses/ValidationError"
  /api/v1/key/market/correlations:
    get:
      tags: [Market]
      summary: Market correlations vs GRI
      responses:
        "200":
          description: Correlation panel
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
  /api/v1/key/market/surprise:
    get:
      tags: [Market]
      summary: Market surprise vs GRI move
      parameters:
        - name: window
          in: query
          schema:
            type: string
            example: 1h
      responses:
        "200":
          description: Surprise reading
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
  /api/v1/key/watch-items:
    get:
      tags: [Watchlist]
      summary: List watch items
      responses:
        "200":
          description: Watch items with enrichment
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/WatchItem"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
    post:
      tags: [Watchlist]
      summary: Create watch item
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WatchItemCreate"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WatchItem"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "422":
          $ref: "#/components/responses/ValidationError"
  /api/v1/key/watch-items/from-driver:
    post:
      tags: [Watchlist]
      summary: Pin a watch item from a risk driver
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [event_id]
              properties:
                event_id:
                  type: string
                  format: uuid
                  description: Geopolitical event whose region becomes a watch pin
      responses:
        "201":
          description: Created from driver
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "422":
          $ref: "#/components/responses/ValidationError"
  /api/v1/key/watch-items/{id}:
    patch:
      tags: [Watchlist]
      summary: Update watch item (label / alerts)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WatchItemUpdate"
      responses:
        "200":
          description: Updated
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "404":
          description: Not found
        "422":
          $ref: "#/components/responses/ValidationError"
    delete:
      tags: [Watchlist]
      summary: Delete watch item
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Deleted
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "404":
          description: Not found
  /api/v1/key/account/usage:
    get:
      tags: [Account]
      summary: Plan, features, limits, and usage
      responses:
        "200":
          description: Account usage snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountUsage"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
components:
  securitySchemes:
    bearerApiKey:
      type: http
      scheme: bearer
      bearerFormat: bt_
      description: |
        API key created in Settings → Integrations. Prefix `bt_`.
        Example header: `Authorization: Bearer bt_…`
  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorMessage"
    PaymentRequired:
      description: Active Pro (or legacy paid) subscription required
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorMessage"
    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
              errors:
                type: object
                additionalProperties:
                  type: array
                  items:
                    type: string
    TooManyRequests:
      description: Rate limit exceeded (see plan rate_limits)
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorMessage"
  schemas:
    ErrorMessage:
      type: object
      properties:
        message:
          type: string
    RiskSnapshot:
      type: object
      properties:
        value:
          type: number
          example: 52
        confidence:
          type: number
          example: 0.82
        components:
          type: array
          items:
            type: object
            properties:
              component:
                type: string
              score:
                type: number
              weight:
                type: number
        metadata:
          type: object
        recorded_at:
          type: string
          format: date-time
    WatchItem:
      type: object
      properties:
        id:
          type: string
          format: uuid
        watchable_type:
          type: string
          enum: [region, entity, symbol]
        watchable_key:
          type: string
        label:
          type: string
          nullable: true
        alerts_enabled:
          type: boolean
        enrichment:
          type: object
          additionalProperties: true
    WatchItemCreate:
      type: object
      required: [type, key]
      properties:
        type:
          type: string
          enum: [region, entity, symbol]
        key:
          type: string
        label:
          type: string
        alerts_enabled:
          type: boolean
        alert_threshold:
          type: number
        alert_channels:
          type: array
          items:
            type: string
            enum: [email, web_push, webhook]
        cooldown_minutes:
          type: integer
    WatchItemUpdate:
      type: object
      properties:
        label:
          type: string
        alerts_enabled:
          type: boolean
        alert_threshold:
          type: number
        alert_channels:
          type: array
          items:
            type: string
        cooldown_minutes:
          type: integer
    AccountUsage:
      type: object
      properties:
        plan:
          type: string
          example: pro
        status:
          type: string
        features:
          type: array
          items:
            type: string
        rate_limits:
          type: object
          properties:
            requests:
              type: integer
            per_minutes:
              type: integer
        limits:
          type: object
          properties:
            alert_rule_limit:
              type: integer
            watch_item_limit:
              type: integer
        usage:
          type: object
          properties:
            alert_rules:
              type: integer
            watch_items:
              type: integer
            api_keys:
              type: integer
        api_key:
          type: object
          properties:
            id:
              type: string
            prefix:
              type: string
            last_used_at:
              type: string
              format: date-time
              nullable: true
    WebhookAlertTriggered:
      type: object
      description: |
        Outbound webhook body for `alert.triggered` (configure URL in Settings → Integrations).
        Verify `X-Blit-Signature: v1=<hmac_sha256(timestamp + "." + raw_body, secret)>` with header `X-Blit-Timestamp`.
      properties:
        type:
          type: string
          example: alert.triggered
        alert_event_id:
          type: string
        rule:
          type: object
          nullable: true
        watch_item:
          type: object
          nullable: true
          description: Present for watchlist-triggered alerts
        payload:
          type: object
          additionalProperties: true
        triggered_at:
          type: string
          format: date-time
x-blit-webhooks:
  description: |
    Outbound only. Create/rotate secrets in the Pro desk Settings → Integrations.
    Headers on every delivery:
      Content-Type: application/json
      X-Blit-Timestamp: <unix seconds>
      X-Blit-Signature: v1=<hex hmac-sha256 of "{timestamp}.{rawBody}">
  event: alert.triggered
