openapi: 3.1.0
info:
  title: Limetry Cloud Action Governance API
  description: |
    HTTP contract for Limetry Cloud (this monorepo): register ActionPolicies,
    evaluate ActionIntents, manage portal rules/tokens/agents, and read billing
    or usage metadata.

    Evaluate semantics follow the open-source Limetry evaluate kernel; this
    surface is the Cloud-hosted operator and organization API (portal JWT or
    scoped bearer API key). Example adapters in OSS CI/commerce/SQL loops prove
    the evaluate contract — they do not define Cloud product endpoints.

    Authentication: operator JWT (portal session) or bearer API key with scopes.
    Mint keys in the Cloud portal or via `limetry setup` against a Cloud base URL.
  version: 0.3.0
  contact:
    url: https://limetry.com

servers:
  - url: https://api.app.limetry.com
    description: |
      Production Limetry Cloud BFF (portal-backed org APIs, evaluate, billing).
  - url: https://api.dev.limetry.com
    description: Cloud BFF preview / dev stack.
  - url: http://localhost:3820
    description: Limetry Cloud BFF / API on local development (default port 3820)

security:
  - bearerAuth: []

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Scoped organization API key from the Cloud portal, or an operator JWT.
        Send as `Authorization: Bearer <token>`.

  schemas:
    ActionIntent:
      type: object
      description: |
        Proposed agent tool call submitted for server-authoritative evaluation.
        Shape matches the open-source Limetry evaluate kernel ActionIntent.
      required: [intent_id, policy_id, agent_id, action_type, resource, issued_at]
      properties:
        intent_id:
          type: string
          format: uuid
          description: Caller-generated unique id for this intent (idempotency / audit key)
        policy_id:
          type: string
          format: uuid
          description: Registered ActionPolicy id to evaluate against
        agent_id:
          type: string
          description: Governed agent identifier as known to the organization
        action_type:
          type: string
          example: "http_post"
          description: Action taxonomy string (e.g. `http_post`, `deploy`, `sql.write`)
        resource:
          type: string
          example: "https://prod.example.com/run"
          description: Target resource URL, path, or logical name for the action
        cost:
          type: object
          description: Optional monetary cost associated with the action
          properties:
            amount_minor:
              type: integer
              description: Cost in minor currency units (e.g. cents)
            currency:
              type: string
              example: "USD"
              description: ISO-4217 currency code
        metadata:
          type: object
          additionalProperties: { type: string }
          description: |
            Optional string-keyed metadata. Do not put secrets or chat transcripts here;
            Cloud stores a scrubbed audit projection.
        issued_at:
          type: string
          format: date-time
          description: ISO-8601 time the intent was created by the caller

    ActionPolicy:
      type: object
      description: |
        ActionPolicy document accepted by register/evaluate. Mirrors the
        open-source Limetry evaluate kernel policy fields used by Cloud evaluate.
      required: [policy_id, version, organization_id, agent_id, allowed_action_types, status, updated_at]
      properties:
        policy_id:
          type: string
          format: uuid
          description: Stable policy identifier
        version:
          type: integer
          minimum: 1
          description: Monotonic policy version
        organization_id:
          type: string
          description: Organization / tenant id owning the policy
        agent_id:
          type: string
          description: Agent this policy primarily governs
        allowed_action_types:
          type: array
          items: { type: string }
          description: Action types explicitly allowed when not denied
        denied_action_types:
          type: array
          items: { type: string }
          description: Action types that always deny
        allowed_resource_patterns:
          type: array
          items: { type: string }
          description: Glob/pattern allowlist for `resource`
        blocked_resource_patterns:
          type: array
          items: { type: string }
          description: Glob/pattern denylist for `resource`
        max_cost_minor:
          type: integer
          description: Maximum allowed `cost.amount_minor` (inclusive)
        currency:
          type: string
          description: Expected cost currency when cost checks apply
        status:
          type: string
          enum: [active, suspended, revoked]
          description: Whether the policy participates in evaluate
        updated_at:
          type: string
          format: date-time
          description: ISO-8601 last update time

    PolicyEvaluationResult:
      type: object
      description: |
        Evaluate outcome: allow, deny, or approval_required, with reasons and an
        optional signed receipt projection.
      properties:
        ok:
          type: boolean
          description: Transport/handler success (distinct from policy allow)
        approved:
          type: boolean
          description: Whether the intent is allowed to proceed without further approval
        reasons:
          type: array
          items: { type: string }
          description: Human-readable evaluate reasons
        decision_id:
          type: string
          format: uuid
          description: Server decision id (also used for approval correlation)
        receipt:
          type: object
          description: Signed outcome receipt for callers that verify digests
          properties:
            decision:
              type: string
              enum: [allow, deny, approval_required]
              description: Final decision enum
            decision_id:
              type: string
              description: Decision id echoed on the receipt
            digest:
              type: string
              description: Integrity digest of the decision payload
            exp:
              type: integer
              description: Receipt expiry as unix seconds
            sig:
              type: string
              description: Signature over the receipt fields

    Rule:
      type: object
      description: |
        Portal governance rule row managed via `/v1/rules`. Condition and action
        are JSON-encoded strings stored by the Cloud BFF.
      properties:
        id:
          type: string
          description: Rule id
        name:
          type: string
          description: Display name
        description:
          type: string
          description: Optional longer description
        condition:
          type: string
          description: JSON-encoded rule condition document
        action:
          type: string
          description: JSON-encoded rule action document
        priority:
          type: integer
          description: Evaluation priority (higher wins on conflict)
        isActive:
          type: boolean
          description: Whether the rule is active
        createdAt:
          type: string
          format: date-time
          description: ISO-8601 creation time
        updatedAt:
          type: string
          format: date-time
          description: ISO-8601 last update time
      required: [id, name, condition, action, priority, isActive, createdAt, updatedAt]

paths:
  /v1/policy/evaluate:
    post:
      operationId: evaluatePolicy
      summary: Evaluate an ActionIntent against a registered ActionPolicy
      description: |
        Server-authoritative evaluation. Returns allow, deny, or approval_required
        with reasons and a signed outcome receipt. Writes a scrubbed audit
        projection (`audit_mode=minimal` by default).

        This is the endpoint used by `RemotePolicyEngine` in the TypeScript SDK
        and by plugin tool loops that call Limetry before irreversible tool use.
      requestBody:
        required: true
        description: Policy id and ActionIntent to evaluate
        content:
          application/json:
            schema:
              type: object
              required: [policy_id, intent]
              properties:
                policy_id:
                  type: string
                  format: uuid
                  description: Registered ActionPolicy id
                intent:
                  $ref: "#/components/schemas/ActionIntent"
      responses:
        "200":
          description: Evaluation result (allow, deny, or approval_required)
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PolicyEvaluationResult" }
        "401":
          description: Missing or invalid bearer credentials
        "400":
          description: Missing or invalid request fields
        "404":
          description: Policy not found for the authenticated organization
        "500":
          description: Unexpected server error during evaluate

  /v1/rules:
    get:
      operationId: listRules
      summary: List governance rules for the authenticated organization
      description: |
        Returns portal rule rows for the caller's organization. Requires an
        operator JWT or API key with rules/read-equivalent access.
      responses:
        "200":
          description: Ordered list of governance rules
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/Rule" }
        "401":
          description: Missing or invalid bearer credentials

    post:
      operationId: createRule
      summary: Create a new governance rule
      description: |
        Creates a portal governance rule with JSON-encoded condition and action
        documents. Priority defaults server-side when omitted.
      requestBody:
        required: true
        description: Rule fields to persist
        content:
          application/json:
            schema:
              type: object
              required: [name, condition, action]
              properties:
                name:
                  type: string
                  description: Display name
                description:
                  type: string
                  description: Optional longer description
                condition:
                  type: string
                  description: JSON-encoded condition document
                action:
                  type: string
                  description: JSON-encoded action document
                priority:
                  type: integer
                  description: Optional evaluation priority
      responses:
        "201":
          description: Created rule
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Rule" }
        "401":
          description: Missing or invalid bearer credentials

  /v1/tokens:
    get:
      operationId: listTokens
      summary: List API tokens for the authenticated user
      description: |
        Returns API tokens for the caller. Token secrets are masked in list
        responses; the full secret is only returned from create.
      responses:
        "200":
          description: List of tokens (secrets masked)
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  description: API token summary row
                  properties:
                    id:
                      type: string
                      description: Token id
                    name:
                      type: string
                      description: Display name
                    token:
                      type: string
                      description: Masked secret for list responses
                    scopes:
                      type: array
                      items: { type: string }
                      description: Granted scopes
                    createdAt:
                      type: string
                      format: date-time
                      description: ISO-8601 creation time
                    expiresAt:
                      type: [string, "null"]
                      format: date-time
                      description: ISO-8601 expiry, or null when non-expiring
        "401":
          description: Missing or invalid bearer credentials

    post:
      operationId: createToken
      summary: Create a scoped API token
      description: |
        Mints a scoped organization API token. The plaintext `token` value is
        returned once; store it securely. Subsequent list calls return a masked form.
      requestBody:
        required: true
        description: Token name, scopes, and optional expiry
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  description: Human-readable token label
                scopes:
                  type: array
                  items: { type: string }
                  description: Scope strings (e.g. `policy:evaluate`, `actions:record`)
                expiresAt:
                  type: string
                  format: date-time
                  description: Optional ISO-8601 expiry
      responses:
        "201":
          description: Created token (token value only shown once)
          content:
            application/json:
              schema:
                type: object
                description: Create-token response including one-time secret
                properties:
                  token:
                    type: string
                    description: Full token value — store it securely; not returned again
                  id:
                    type: string
                    description: Token row id
        "401":
          description: Missing or invalid bearer credentials

  /v1/tokens/{tokenId}:
    delete:
      operationId: revokeToken
      summary: Revoke an API token
      description: |
        Permanently revokes the token so it can no longer authenticate. Idempotent
        for already-revoked ids may still return 204 depending on server policy.
      parameters:
        - in: path
          name: tokenId
          required: true
          description: Token id to revoke
          schema: { type: string }
      responses:
        "204":
          description: Token revoked
        "401":
          description: Missing or invalid bearer credentials
        "404":
          description: Token not found

  /v1/rules/{ruleId}:
    patch:
      operationId: updateRule
      summary: Update a governance rule
      description: |
        Partially updates a portal governance rule. Omitted fields are left unchanged.
      parameters:
        - in: path
          name: ruleId
          required: true
          description: Rule id to update
          schema: { type: string }
      requestBody:
        required: false
        description: Partial rule fields
        content:
          application/json:
            schema:
              type: object
              description: Patchable rule fields
              properties:
                name:
                  type: string
                  description: Updated display name
                description:
                  type: string
                  description: Updated description
                condition:
                  type: string
                  description: Updated JSON-encoded condition
                action:
                  type: string
                  description: Updated JSON-encoded action
                priority:
                  type: integer
                  description: Updated priority
                isActive:
                  type: boolean
                  description: Whether the rule remains active
      responses:
        "200":
          description: Updated rule
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Rule" }
        "401":
          description: Missing or invalid bearer credentials
        "404":
          description: Rule not found
    delete:
      operationId: deleteRule
      summary: Delete a governance rule
      description: Permanently deletes the rule for the authenticated organization.
      parameters:
        - in: path
          name: ruleId
          required: true
          description: Rule id to delete
          schema: { type: string }
      responses:
        "204":
          description: Rule deleted
        "401":
          description: Missing or invalid bearer credentials
        "404":
          description: Rule not found

  /health:
    get:
      operationId: health
      summary: Health check
      description: |
        Liveness probe for load balancers and local preflight. Does not require
        authentication and does not exercise evaluate or database connectivity
        beyond process readiness.
      security: []
      responses:
        "200":
          description: Service process is healthy
          content:
            application/json:
              schema:
                type: object
                description: Minimal health payload
                properties:
                  ok:
                    type: boolean
                    description: Always true on success
                  service:
                    type: string
                    description: Service name identifier

  /v1/policies/register:
    post:
      operationId: registerGovernancePolicy
      summary: Register an ActionPolicy with the evaluation engine
      description: |
        Persists an ActionPolicy document for subsequent `/v1/policy/evaluate`
        calls. Validates required policy fields before storage.
      requestBody:
        required: true
        description: ActionPolicy envelope
        content:
          application/json:
            schema:
              type: object
              required: [policy]
              properties:
                policy:
                  $ref: "#/components/schemas/ActionPolicy"
      responses:
        "201":
          description: Policy registered successfully
        "400":
          description: Invalid or incomplete policy document
        "401":
          description: Missing or invalid bearer credentials

  /v1/governance/authorize:
    post:
      operationId: authorizeActionIntent
      summary: Legacy alias — maps to ActionIntent evaluate
      description: |
        Compatibility alias for evaluate. Accepts the same `{ policy_id, intent }`
        body as `/v1/policy/evaluate`. Prefer `/v1/policy/evaluate` for new callers.
        May return 403 when the organization plan limit or policy denies the call.
      requestBody:
        required: true
        description: Policy id and ActionIntent (same shape as evaluate)
        content:
          application/json:
            schema:
              type: object
              required: [policy_id, intent]
              properties:
                policy_id:
                  type: string
                  format: uuid
                  description: Registered ActionPolicy id
                intent:
                  $ref: "#/components/schemas/ActionIntent"
      responses:
        "200":
          description: Evaluation / authorization result
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PolicyEvaluationResult" }
        "403":
          description: Policy violation or plan usage limit exceeded
        "401":
          description: Missing or invalid bearer credentials

  /v1/authorizations:
    get:
      operationId: listAuthorizations
      summary: List organization evaluate and receipt history
      description: |
        Returns recent authorization / evaluate history for the authenticated
        organization, including scrubbed request/response projections when stored.
      responses:
        "200":
          description: Authorization history with optional settlement details
        "401":
          description: Missing or invalid bearer credentials

  /v1/authorizations/export:
    get:
      operationId: exportAuthorizations
      summary: Export business-plan authorization history as CSV
      description: |
        Streams authorization history as CSV for audit export. Requires a Business
        plan (or equivalent entitlement); otherwise returns 403.
      responses:
        "200":
          description: CSV audit export body
          content:
            text/csv:
              schema:
                type: string
                description: CSV rows of authorization history
        "403":
          description: Business plan (or export entitlement) required
        "401":
          description: Missing or invalid bearer credentials

  /v1/policies:
    get:
      operationId: listPolicies
      summary: List persisted organization policy versions
      description: |
        Lists ActionPolicy versions stored for the authenticated organization,
        including status and document metadata as returned by the Cloud API.
      responses:
        "200":
          description: Organization policies
        "401":
          description: Missing or invalid bearer credentials

  /v1/agents:
    get:
      operationId: listAgents
      summary: List governed agents and kill-switch status
      description: |
        Returns agents registered for the organization, including whether each is
        `active` or `disabled` (kill switch).
      responses:
        "200":
          description: Organization agents
        "401":
          description: Missing or invalid bearer credentials
    post:
      operationId: createAgent
      summary: Register a governed agent
      description: |
        Creates an agent identity used in ActionIntent `agent_id` and policy
        bindings. Optional `externalId` correlates to an upstream system id.
      requestBody:
        required: true
        description: Agent name and optional external id
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  description: Display name
                externalId:
                  type: string
                  description: Optional external correlation id (unique per organization)
      responses:
        "201":
          description: Agent registered
        "401":
          description: Missing or invalid bearer credentials
        "400":
          description: Invalid agent payload

  /v1/agents/{agentId}/status:
    patch:
      operationId: updateAgentStatus
      summary: Enable or disable an agent
      description: |
        Sets the agent kill switch. Disabled agents should fail closed on evaluate
        paths that honor agent status.
      parameters:
        - in: path
          name: agentId
          required: true
          description: Agent id to update
          schema: { type: string }
      requestBody:
        required: true
        description: Target agent status
        content:
          application/json:
            schema:
              type: object
              required: [status]
              properties:
                status:
                  type: string
                  enum: [active, disabled]
                  description: "`active` enables; `disabled` kills the agent"
      responses:
        "200":
          description: Agent status updated
        "401":
          description: Missing or invalid bearer credentials
        "404":
          description: Agent not found

  /v1/hosted/config:
    get:
      operationId: getHostedConfig
      summary: Public hosted Free API base URL and Free plan limits for connect-agent UX
      description: |
        Returns public Cloud Free-plan connect configuration: API and portal base
        URLs plus Free meter limits used by connect-agent UX. May be callable with
        reduced auth depending on deployment; treat fields as non-secret.
      responses:
        "200":
          description: Hosted freemium configuration for connect flows

  /v1/usage/summary:
    get:
      operationId: getUsageSummary
      summary: Return plan meters, retention, and upgrade hints
      description: |
        Returns the organization's current plan meters (used/limit/remaining),
        retention days, and optional upgrade recommendation when Free limits are near.
      responses:
        "200":
          description: Usage summary for the authenticated organization
        "401":
          description: Missing or invalid bearer credentials

  /v1/ops/usage:
    get:
      operationId: getOpsUsage
      summary: Operator cost and abuse snapshot (service bearer only)
      description: |
        Internal operator endpoint returning aggregate Free vs paid usage meters
        for abuse and cost monitoring. Requires a service bearer, not an org API key.
      responses:
        "200":
          description: Aggregate Free vs paid meters
        "401":
          description: Missing or invalid service bearer

  /v1/billing/subscription:
    get:
      operationId: getSubscription
      summary: Return the organization subscription and plan
      description: |
        Returns the Cloud tenant's Stripe-backed subscription snapshot (`plan`,
        `status`, and related fields as implemented by the BFF).
      responses:
        "200":
          description: Current subscription for the authenticated organization
        "401":
          description: Missing or invalid bearer credentials

  /v1/billing/checkout:
    post:
      operationId: createCheckout
      summary: Create a Stripe subscription checkout
      description: |
        Creates a Stripe Checkout session for upgrading to `pro` or `business`.
        Returns a hosted Checkout URL for the browser to open.
      requestBody:
        required: true
        description: Target paid plan
        content:
          application/json:
            schema:
              type: object
              required: [plan]
              properties:
                plan:
                  type: string
                  enum: [pro, business]
                  description: Paid plan to purchase
      responses:
        "201":
          description: Stripe Checkout URL created
        "401":
          description: Missing or invalid bearer credentials
        "400":
          description: Invalid plan or billing not configured

  /v1/billing/portal:
    post:
      operationId: createBillingPortal
      summary: Create a Stripe customer portal session
      description: |
        Creates a Stripe Customer Portal session so the organization can manage
        payment methods and subscription details.
      responses:
        "200":
          description: Stripe customer portal URL
        "401":
          description: Missing or invalid bearer credentials
        "400":
          description: No Stripe customer for the organization or billing not configured
