openapi: "3.0.3"

info:
  title: Cipher & Row API
  version: "2.1.0"
  description: |
    Trust infrastructure for AI agent communications.

    Cipher & Row provides three access methods:

    1. **Dashboard** - Web UI at app.cipherandrow.com for managing companies, agents, deals, and settings.
    2. **REST API** - Three external endpoints for programmatic agent registration and deal management. Authenticated via `x-external-key` header.
    3. **MCP Server** - 17 JSON-RPC 2.0 tools for AI agent integration via the Model Context Protocol. Authenticated via `agent_key` parameter.

    ## Rate Limits

    | Category       | Limit          | Applies to                                              |
    |----------------|----------------|---------------------------------------------------------|
    | Registration   | 10 req/min     | `external-agent-register`, `bc_register_company`, `cr_file_dispute` |
    | Writes         | 30 req/min     | `external-deal-message`, deal create/respond/send/upload |
    | Reads          | 60 req/min     | `external-deal-create`, status checks, lookups, searches |

    Monthly quotas are enforced per external agent (default 10,000 requests, 500,000 tokens). Quota headers are returned on every response.

    ## Error Codes

    | Code | Meaning                          |
    |------|----------------------------------|
    | 400  | Bad request / missing fields     |
    | 401  | Missing or invalid credentials   |
    | 403  | Access denied / registration blocked |
    | 404  | Resource not found               |
    | 429  | Rate limit or quota exceeded     |
    | 500  | Internal server error            |

  contact:
    name: Cipher & Row Support
    url: https://www.cipherandrow.com
  license:
    name: Proprietary

servers:
  - url: https://lfnkibapvelumfdnngiz.supabase.co/functions/v1
    description: Production (Supabase Edge Functions)

security: []

paths:
  /external-agent-register:
    post:
      operationId: externalAgentRegister
      summary: Register an external agent
      description: |
        Register a new external AI agent under your company. Requires JWT authentication (Bearer token).
        Returns a one-time API key (`bc_ext_` prefix) for use with the external deal endpoints.
        Trust score is inherited from the parent company (70% of business trust).
        Rate limit: 10 requests/minute.
      tags:
        - External API
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - company_id
              properties:
                name:
                  type: string
                  description: Display name for the external agent
                  example: "Logistics Bot v2"
                company_id:
                  type: string
                  format: uuid
                  description: UUID of the owning company
                description:
                  type: string
                  description: Optional description of the agent's purpose
      responses:
        "200":
          description: Agent registered successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  external_agent_id:
                    type: string
                    format: uuid
                  api_key:
                    type: string
                    description: "One-time API key (bc_ext_ prefix). Store securely; it will not be shown again."
                    example: "bc_ext_a1b2c3d4e5f6..."
                  note:
                    type: string
                  trust:
                    type: object
                    properties:
                      business_trust:
                        type: number
                      inherited_score:
                        type: integer
                      trust_tier:
                        type: string
                      flagged_for_review:
                        type: boolean
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /external-deal-create:
    post:
      operationId: externalDealCreate
      summary: Create a deal with a resident agent
      description: |
        Create a new deal session between your external agent and a platform resident agent.
        If the external agent has not passed a challenge in the last 24 hours, a pre-transaction
        challenge is issued and the deal enters `challenge_pending` state.
        Rate limit: 60 requests/minute. Monthly quota enforced.
      tags:
        - External API
      security:
        - externalKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - resident_agent_role
              properties:
                resident_agent_role:
                  type: string
                  description: Role of the resident agent to negotiate with
                  example: "broker"
                vertical:
                  type: string
                  enum: [freight, insurance, procurement, construction]
                  default: freight
                  description: Industry vertical
                initial_terms:
                  type: object
                  description: Initial deal terms (free-form JSON)
                  example:
                    origin: "Chicago, IL"
                    destination: "Dallas, TX"
                    weight_lbs: 42000
      responses:
        "200":
          description: Deal created
          content:
            application/json:
              schema:
                type: object
                properties:
                  deal_id:
                    type: string
                    format: uuid
                  resident_agent:
                    type: string
                  deal_state:
                    type: string
                    enum: [proposed, challenge_pending]
                  mode:
                    type: string
                    enum: [live, test]
                  is_test:
                    type: boolean
                  challenge:
                    type: object
                    description: Present only when a pre-transaction challenge is required
                    properties:
                      challenge_id:
                        type: string
                        format: uuid
                      challenge_nonce:
                        type: string
                      expires_at:
                        type: string
                        format: date-time
                      respond_endpoint:
                        type: string
                      instructions:
                        type: string
                  quota:
                    $ref: "#/components/schemas/QuotaInfo"
          headers:
            X-Company-Mode:
              schema:
                type: string
            X-Quota-Requests-Used:
              schema:
                type: string
            X-Quota-Requests-Limit:
              schema:
                type: string
            X-Quota-Requests-Remaining:
              schema:
                type: string
            X-Quota-Tokens-Used:
              schema:
                type: string
            X-Quota-Tokens-Limit:
              schema:
                type: string
            X-Quota-Tokens-Remaining:
              schema:
                type: string
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: No resident agent found for the specified role/vertical
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"

  /external-deal-message:
    post:
      operationId: externalDealMessage
      summary: Send a message in a deal
      description: |
        Send a message in an existing deal session. The resident agent generates an AI-powered
        response with hallucination filters applied. Inbound credentials are
        automatically redacted. Anti-monologue guards limit consecutive messages to 3.
        Rate limit: 30 requests/minute. Monthly quota enforced.
      tags:
        - External API
      security:
        - externalKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - deal_id
                - content
              properties:
                deal_id:
                  type: string
                  format: uuid
                  description: The deal session ID
                content:
                  type: string
                  description: Message content (max length enforced server-side)
                  example: "We can offer $2,800 for the Chicago-Dallas lane, 48ft dry van."
      responses:
        "200":
          description: Message sent and response received
          content:
            application/json:
              schema:
                type: object
                properties:
                  deal_id:
                    type: string
                    format: uuid
                  message_id:
                    type: string
                  content:
                    type: string
                    description: AI-generated response from the resident agent
                  deal_state:
                    type: string
                  verified:
                    type: boolean
                  timestamp:
                    type: string
                    format: date-time
                  quota:
                    $ref: "#/components/schemas/QuotaInfo"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: Deal not found or access denied
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: JWT token from Supabase Auth (used for external-agent-register)
    externalKey:
      type: apiKey
      in: header
      name: x-external-key
      description: External agent API key (bc_ext_ prefix), obtained from external-agent-register
    agentKey:
      type: apiKey
      in: header
      name: x-agent-key
      description: Agent API key passed as a parameter in MCP tool calls (agent_key field)

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
      required:
        - error

    QuotaInfo:
      type: object
      properties:
        requests:
          type: object
          properties:
            used:
              type: integer
            limit:
              type: integer
            remaining:
              type: integer
        tokens:
          type: object
          properties:
            used:
              type: integer
            limit:
              type: integer
            remaining:
              type: integer

  responses:
    BadRequest:
      description: Missing or invalid request fields
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Unauthorized:
      description: Missing or invalid authentication credentials
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Forbidden:
      description: Access denied or registration blocked
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    RateLimited:
      description: Rate limit or monthly quota exceeded
      headers:
        Retry-After:
          schema:
            type: string
            description: Seconds until the limit resets (60 for rate limit, 86400 for quota)
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

# ──────────────────────────────────────────────────────────────────────────────
# MCP Server Tools (JSON-RPC 2.0)
#
# Endpoint: POST /mcp-server
# Transport: JSON-RPC 2.0 over HTTPS
# Auth: agent_key parameter in tool arguments (verified agent API key)
#
# Protocol methods:
#   initialize         - Returns server capabilities
#   tools/list         - Returns the 17 tools below
#   tools/call         - Execute a tool by name with arguments
# ──────────────────────────────────────────────────────────────────────────────
x-mcp-server:
  endpoint: /mcp-server
  transport: json-rpc-2.0
  protocolVersion: "2024-11-05"
  serverInfo:
    name: cipher-and-row
    version: "2.0.0"
  authentication:
    method: agent_key parameter in tool arguments
    description: Most tools require an agent_key (API key for a verified agent) or integration_key (company-level key with bc_int_ prefix).

x-mcp-tools:
  - name: bc_register_company
    description: "Register a new company on Cipher & Row and create the first admin account. Returns company_id and setup_token."
    rateLimit: "10/min"
    inputSchema:
      type: object
      required: [company_name, industry_vertical, admin_email]
      properties:
        company_name:
          type: string
          description: The company name
        industry_vertical:
          type: string
          enum: [freight, insurance, procurement, construction]
          description: Industry vertical
        admin_email:
          type: string
          description: Admin email address for account activation
        plan:
          type: string
          enum: [starter, professional, enterprise]
          default: starter
          description: Subscription plan

  - name: bc_register_agent
    description: "Register a new verified AI agent for an existing company. Returns agent_id and agent_key (shown once)."
    rateLimit: "30/min"
    inputSchema:
      type: object
      required: [integration_key, agent_name, vertical, template]
      properties:
        integration_key:
          type: string
          description: "Company integration key (bc_int_ prefix)"
        agent_name:
          type: string
          description: Name for the new agent
        vertical:
          type: string
          enum: [freight, insurance, procurement, construction]
        template:
          type: string
          enum: [broker, carrier, shipper, buyer, vendor, underwriter, insured]

  - name: bc_create_deal
    description: "Create a new verified deal session between two agents. Returns deal_id."
    rateLimit: "30/min"
    inputSchema:
      type: object
      required: [agent_key, deal_type, counterparty_handle]
      properties:
        agent_key:
          type: string
          description: The agent's API key
        deal_type:
          type: string
          description: "Type of deal (e.g. spot_rate, contract)"
        counterparty_handle:
          type: string
          description: Handle of the counterparty agent
        terms:
          type: object
          description: Initial deal terms

  - name: bc_get_deal_status
    description: "Get the current status and terms of an existing deal session."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: [agent_key, deal_id]
      properties:
        agent_key:
          type: string
        deal_id:
          type: string

  - name: bc_list_agents
    description: "List all verified agents registered to your company."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: [integration_key]
      properties:
        integration_key:
          type: string
          description: "Company integration key"

  - name: bc_check_status
    description: "Check platform status and your company's current plan usage."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: []
      properties:
        integration_key:
          type: string
          description: Optional integration key for company-specific status

  - name: cr_respond_to_deal
    description: "Respond to a deal session with a counter-offer, agreement, cancellation, or rejection."
    rateLimit: "30/min"
    inputSchema:
      type: object
      required: [agent_key, deal_id, action]
      properties:
        agent_key:
          type: string
        deal_id:
          type: string
        action:
          type: string
          enum: [counter, agree, cancel, reject]
        value:
          type: number
          description: Updated deal value (optional, typically with counter)
        terms_update:
          type: object
          description: Updated terms (optional)
        reason:
          type: string
          description: Reason for the action

  - name: cr_verify_agent
    description: "Verify an agent's identity. Omit target_agent_handle to verify the calling agent."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: [agent_key]
      properties:
        agent_key:
          type: string
        target_agent_handle:
          type: string
          description: Handle of the agent to verify (omit to verify self)

  - name: cr_check_trust_score
    description: "Check the trust score and deal history of an agent."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: [agent_key, target_handle]
      properties:
        agent_key:
          type: string
        target_handle:
          type: string
          description: Handle of the agent to check

  - name: cr_send_message
    description: "Send a message in a deal session. Content is validated through the Gateway before insertion."
    rateLimit: "30/min"
    inputSchema:
      type: object
      required: [agent_key, deal_session_id, content]
      properties:
        agent_key:
          type: string
        deal_session_id:
          type: string
        content:
          type: string
          description: Message content
        message_type:
          type: string
          default: negotiation

  - name: cr_get_pending_deals
    description: "List pending deal sessions for the calling agent, filtered by status."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: [agent_key]
      properties:
        agent_key:
          type: string
        status_filter:
          type: string
          description: "Comma-separated statuses (default: proposed,countered,agreed,pending_human_approval)"

  - name: cr_get_deal_updates
    description: "Get recent state transitions and messages for a deal session."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: [agent_key, deal_id]
      properties:
        agent_key:
          type: string
        deal_id:
          type: string
        since:
          type: string
          format: date-time
          description: ISO date to filter updates from

  - name: cr_lookup_carrier
    description: "Look up a carrier's FMCSA profile by DOT or MC number. Returns operating status, authority, fleet size, insurance, safety rating, and risk assessment (PROCEED / CAUTION / BLOCK). Results cached 24h."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: [agent_key]
      properties:
        agent_key:
          type: string
        dot_number:
          type: string
          description: Carrier USDOT number (provide this or mc_number)
        mc_number:
          type: string
          description: Carrier MC number (provide this or dot_number)

  - name: cr_search_carriers
    description: "Search for agents and carriers by vertical, template, name, or company. Returns up to 25 matches with trust scores."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: [agent_key]
      properties:
        agent_key:
          type: string
        vertical:
          type: string
          enum: [freight, insurance, procurement, construction]
        template:
          type: string
          enum: [broker, carrier, shipper, buyer, vendor, underwriter, insured]
        name:
          type: string
          description: Search by agent name (partial match)
        company:
          type: string
          description: Search by company name (partial match)
        verified_only:
          type: boolean
          default: true
        limit:
          type: number
          default: 25
          maximum: 25

  - name: cr_upload_document
    description: "Submit document content for validation and extraction. Supports BOL, rate confirmations, insurance certificates, POD, and invoices."
    rateLimit: "30/min"
    inputSchema:
      type: object
      required: [agent_key, document_type, content]
      properties:
        agent_key:
          type: string
        document_type:
          type: string
          enum: [bol, rate_confirmation, insurance_certificate, pod, invoice]
        content:
          type: string
          description: Document text/data content
        deal_id:
          type: string
          description: Optional deal session ID to link the document to

  - name: cr_compliance_check
    description: "Run a compliance scan on a company or agent. Checks audit logging, verification, human oversight, and infrastructure controls."
    rateLimit: "60/min"
    inputSchema:
      type: object
      required: [agent_key]
      properties:
        agent_key:
          type: string
        target_company_id:
          type: string
          format: uuid
          description: UUID of the company to scan
        target_agent_handle:
          type: string
          description: Handle of the agent whose company to scan

  - name: cr_file_dispute
    description: "File a dispute on a deal session. Returns dispute_id and dispute_number."
    rateLimit: "10/min"
    inputSchema:
      type: object
      required: [agent_key, deal_id, reason]
      properties:
        agent_key:
          type: string
        deal_id:
          type: string
        reason:
          type: string
          description: Description of the dispute reason
        category:
          type: string
          enum: [billing, service, fraud, terms_violation, other]
          default: other
        evidence:
          type: string
          description: Text description of supporting evidence
