openapi: 3.0.3
info:
  title: Tradedly Platform Runtime Authorisation API
  version: "1.0"
  description: >
    Runtime authorisation for AI-initiated real-world execution. Platforms MUST obtain a Tradedly
    Authorisation Token prior to enabling execution. If decision != permitted, execution must not proceed.
servers:
  - url: https://api.tradedly.com/v1
security:
  - ApiKeyAuth: []
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
      description: Stable key for identical intent; prevents duplicate permits.
  schemas:
    PermitDecision:
      type: string
      enum: [permitted, denied, constrained]
    TokenState:
      type: string
      enum: [issued, revoked, expired]
    AttemptState:
      type: string
      enum: [pending, dispatched, accepted, declined, no_coverage, cancelled, completed, not_found]
    ExecutionConstraints:
      type: object
      additionalProperties: false
      properties:
        sla_minutes:
          type: integer
          minimum: 1
        max_retries:
          type: integer
          minimum: 0
          maximum: 5
        cancellation_wins_until:
          type: string
          enum: [completed]
        spend_cap_minor_units:
          type: integer
          minimum: 0
          description: Optional cap (e.g., pence/cents). 0 means no spend allowed.
        requires_human_ack:
          type: boolean
          description: If true, human_approval_id is required when creating an attempt.
      required: [sla_minutes, max_retries, cancellation_wins_until]
    Intent:
      type: object
      additionalProperties: false
      properties:
        intent_id:
          type: string
          description: Platform-generated stable ID for the user request.
        customer_id:
          type: string
          description: Platform customer tenant ID.
        agent_id:
          type: string
          description: Agent identifier (model/workflow).
        action_type:
          type: string
          enum: [dispatch_attempt, schedule_attempt, commit_attempt, purchase_attempt, message_attempt]
        domain:
          type: string
          description: e.g., claims_property, facilities, logistics
        region:
          type: string
        context:
          type: object
          additionalProperties: true
          description: Opaque metadata (claim_ref, incident_type, priority, etc.)
        constraints:
          $ref: "#/components/schemas/ExecutionConstraints"
      required: [intent_id, customer_id, agent_id, action_type, domain, constraints]
    AuthorisationToken:
      type: object
      additionalProperties: false
      properties:
        token_id:
          type: string
        state:
          $ref: "#/components/schemas/TokenState"
        decision:
          $ref: "#/components/schemas/PermitDecision"
        issued_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
        constraints:
          $ref: "#/components/schemas/ExecutionConstraints"
        denial_reason:
          type: string
          nullable: true
        audit_id:
          type: string
        evidence_retention_days:
          type: integer
      required: [token_id, state, decision, issued_at, expires_at, constraints, audit_id, evidence_retention_days]
    Attempt:
      type: object
      additionalProperties: false
      properties:
        attempt_id:
          type: string
        token_id:
          type: string
        status:
          $ref: "#/components/schemas/AttemptState"
        eta_minutes:
          type: integer
          nullable: true
        next_update_by:
          type: string
          format: date-time
          nullable: true
        notes_for_requester:
          type: string
          nullable: true
        updated_at:
          type: string
          format: date-time
      required: [attempt_id, token_id, status, updated_at]
    EvidenceEvent:
      type: object
      additionalProperties: false
      properties:
        event_id:
          type: string
        audit_id:
          type: string
        at:
          type: string
          format: date-time
        type:
          type: string
          enum: [token_issued, token_denied, token_revoked, attempt_created, attempt_dispatched,
                 provider_accepted, provider_declined, no_coverage, cancelled, completed]
        payload:
          type: object
          additionalProperties: true
      required: [event_id, audit_id, at, type, payload]
    Error:
      type: object
      additionalProperties: false
      properties:
        error:
          type: string
        message:
          type: string
      required: [error, message]
paths:
  /platform/authorisations:
    post:
      summary: Issue runtime authorisation token (required before execution enablement).
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Intent"
      responses:
        "201":
          description: Token issued with decision.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthorisationToken"
        "409":
          description: Conflicting retry for same Idempotency-Key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /platform/attempts:
    post:
      summary: Create an execution attempt under an existing permitted token.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                token_id:
                  type: string
                human_approval_id:
                  type: string
                  nullable: true
                  description: Required if constraints.requires_human_ack=true
              required: [token_id]
      responses:
        "201":
          description: Attempt created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Attempt"
        "403":
          description: Token not permitted / revoked / expired / missing human approval.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /platform/attempts/{attempt_id}:
    get:
      summary: Get attempt status (safe-by-default).
      parameters:
        - name: attempt_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Attempt.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Attempt"
  /platform/authorisations/{token_id}/revoke:
    post:
      summary: Revoke a token (hard stop).
      parameters:
        - name: token_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Token revoked.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthorisationToken"
  /platform/audit/{audit_id}/events:
    get:
      summary: Retrieve audit evidence events for governance and review.
      parameters:
        - name: audit_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Evidence events.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/EvidenceEvent"
