{
  "openapi": "3.1.0",
  "info": {
    "title": "patxin API",
    "description": "Real-time API for AI agents to request qualified human judgment on demand. Patch in a human for cognitive micro-tasks: legal review, creative feedback, ethical decisions, domain expertise. Matching in under 60 seconds.",
    "version": "1.0.0",
    "contact": {
      "name": "patxin",
      "url": "https://patxin.com",
      "email": "alex@patxin.com"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.patxin.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/v1/account/limits": {
      "get": {
        "operationId": "getAccountLimits",
        "summary": "Get account limits and usage",
        "description": "Returns credits remaining, daily/monthly budget, rate limits, concurrent active requests, and allowed skills for the authenticated API key.",
        "tags": ["Account"],
        "responses": {
          "200": {
            "description": "Account limits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountLimits"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/account/policy": {
      "get": {
        "operationId": "getAccountPolicy",
        "summary": "Get company policy rules",
        "description": "Returns the resolved policy for this API key's project: confidentiality level, PII redaction, forbidden topics, routing mode, allowed response formats.",
        "tags": ["Account"],
        "responses": {
          "200": {
            "description": "Policy details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPolicy"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/estimate": {
      "post": {
        "operationId": "getEstimate",
        "summary": "Get price estimate",
        "description": "Estimate the cost of a request before creating it. Price depends on urgency and patxer availability (scarcity multiplier).",
        "tags": ["Requests"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Price estimate",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Estimate"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/requests": {
      "post": {
        "operationId": "createRequest",
        "summary": "Create a human request",
        "description": "Request a qualified human for a cognitive micro-task. The system matches a patxer in under 60 seconds. Credits are escrowed immediately. Use test keys (pk_test_*) to simulate without real humans or credit spend.",
        "tags": ["Requests"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRequestInput"
              },
              "example": {
                "skill": "legal_review",
                "context": "Review this clause for compliance with Spanish labor law: ...",
                "max_price": "15.00EUR",
                "urgency": "15min",
                "duration": "15min",
                "category": "domain_expertise"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Request created, matching in progress",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateRequestResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": {
            "description": "Request rejected (insufficient credits, budget exceeded, skill not allowed, or flagged content)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listRequests",
        "summary": "List requests",
        "description": "List requests with optional filters by status, skill, and date range. Ordered by creation date descending.",
        "tags": ["Requests"],
        "parameters": [
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["matching", "matched", "in_progress", "completed", "expired", "cancelled", "disputed"] } },
          { "name": "skill", "in": "query", "schema": { "type": "string" } },
          { "name": "from", "in": "query", "schema": { "type": "string", "format": "date-time" }, "description": "Filter from this date (inclusive)" },
          { "name": "to", "in": "query", "schema": { "type": "string", "format": "date-time" }, "description": "Filter to this date (inclusive)" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } }
        ],
        "responses": {
          "200": {
            "description": "List of requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RequestList"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/requests/{id}": {
      "get": {
        "operationId": "getRequest",
        "summary": "Get request status and result",
        "description": "Get the current status of a request. When completed, includes the patxer's result and human quality metrics. Requests auto-expire based on urgency level.",
        "tags": ["Requests"],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Request ID (e.g., req_xxx)" }
        ],
        "responses": {
          "200": {
            "description": "Request details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RequestDetail"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/requests/{id}/cancel": {
      "post": {
        "operationId": "cancelRequest",
        "summary": "Cancel a request",
        "description": "Cancel a request in 'matching' or 'matched' state. Credits are refunded immediately.",
        "tags": ["Requests"],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Request cancelled, credits refunded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/requests/{id}/rate": {
      "post": {
        "operationId": "rateRequest",
        "summary": "Rate a completed task",
        "description": "Rate the quality of a completed task (1-5). Ratings affect patxer matching priority for future requests.",
        "tags": ["Requests"],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rating recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateResponse"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/requests/{id}/dispute": {
      "post": {
        "operationId": "openDispute",
        "summary": "Open a dispute",
        "description": "Dispute a completed task within 24 hours. Tasks under €5 with low rating get automatic 50% refund. Tasks ≥€5 go through AI triage (Tier 0), with optional escalation to peer jury (Tier 1) and board review (Tier 3).",
        "tags": ["Disputes"],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DisputeInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Dispute created (may be auto-resolved or AI-reviewed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisputeResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": {
            "description": "Dispute already exists for this request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getDisputeStatus",
        "summary": "Get dispute status",
        "description": "Check the status of a dispute, including AI ruling, jury result, escalation history, and resolution details.",
        "tags": ["Disputes"],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Dispute details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisputeDetail"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/health": {
      "get": {
        "operationId": "healthCheck",
        "summary": "Health check",
        "description": "Verify the API is operational. No authentication required.",
        "tags": ["System"],
        "security": [],
        "responses": {
          "200": {
            "description": "API is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": { "type": "string", "example": "ok" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/ws": {
      "get": {
        "operationId": "websocket",
        "summary": "WebSocket connection for real-time updates",
        "description": "Connect via WebSocket for real-time request status updates. Pass API key as query parameter. Events: request.matching, request.in_progress, request.completed, request.expired, request.cancelled.",
        "tags": ["System"],
        "parameters": [
          { "name": "token", "in": "query", "required": true, "schema": { "type": "string" }, "description": "API key (pk_live_* or pk_test_*)" }
        ],
        "responses": {
          "101": {
            "description": "WebSocket upgrade"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key with pk_live_* (production) or pk_test_* (test mode) prefix"
      }
    },
    "schemas": {
      "AccountLimits": {
        "type": "object",
        "properties": {
          "credits_remaining": { "type": "number" },
          "per_request_max": { "type": "number" },
          "daily_remaining": { "type": "number" },
          "daily_max": { "type": "number" },
          "daily_spent": { "type": "number" },
          "rate_limit": { "type": "integer", "description": "Requests per hour" },
          "monthly_budget": { "type": "number" },
          "monthly_spent": { "type": "number" },
          "monthly_remaining": { "type": "number" },
          "concurrent_active": { "type": "integer" },
          "allowed_skills": { "type": ["array", "null"], "items": { "type": "string" } }
        }
      },
      "AccountPolicy": {
        "type": "object",
        "properties": {
          "confidentiality_level": { "type": "string", "enum": ["open", "standard", "confidential", "strictly_confidential"] },
          "can_share_code": { "type": "boolean" },
          "can_share_full_files": { "type": "boolean" },
          "can_share_urls": { "type": "boolean" },
          "can_share_images": { "type": "boolean" },
          "max_context_chars": { "type": "integer" },
          "pii_auto_redact": { "type": "boolean" },
          "forbidden_topics": { "type": "array", "items": { "type": "string" } },
          "allowed_response_formats": { "type": "array", "items": { "type": "string", "enum": ["free_text", "structured_json", "checklist", "rating_scale"] } },
          "require_nda": { "type": "boolean" },
          "company_name_visible": { "type": "boolean" },
          "data_retention_days": { "type": "integer" },
          "routing_mode": { "type": "string", "enum": ["external_only", "internal_first", "internal_only"] },
          "no_ai_tools": { "type": "boolean" },
          "allowed_skills": { "type": ["array", "null"], "items": { "type": "string" } },
          "max_price_per_request": { "type": "string" }
        }
      },
      "EstimateInput": {
        "type": "object",
        "required": ["skill", "urgency", "duration"],
        "properties": {
          "skill": { "type": "string", "minLength": 1 },
          "urgency": { "type": "string", "enum": ["1min", "5min", "15min", "30min"] },
          "duration": { "type": "string", "pattern": "^\\d+(min|h)$" }
        }
      },
      "Estimate": {
        "type": "object",
        "properties": {
          "estimated_price": { "type": "string", "example": "7.50EUR" },
          "currency": { "type": "string", "example": "EUR" },
          "available_patxers": { "type": "integer" },
          "estimated_match_time": { "type": "string", "example": "45s" }
        }
      },
      "CreateRequestInput": {
        "type": "object",
        "required": ["skill", "urgency", "duration", "context", "max_price"],
        "properties": {
          "skill": { "type": "string", "minLength": 1 },
          "category": { "type": "string", "enum": ["verification", "classification", "creative", "domain_expertise", "ethical", "cultural", "visual_qa", "emotional"] },
          "urgency": { "type": "string", "enum": ["1min", "5min", "15min", "30min"] },
          "duration": { "type": "string", "pattern": "^\\d+(min|h)$" },
          "context": { "type": "string", "minLength": 1, "maxLength": 2000 },
          "max_price": { "type": "string", "pattern": "^\\d+(\\.\\d{2})?[A-Z]{3}$", "example": "15.00EUR" },
          "information_policy": { "type": "string", "enum": ["open", "redacted", "sandboxed"], "default": "redacted" }
        }
      },
      "CreateRequestResponse": {
        "type": "object",
        "properties": {
          "request_id": { "type": "string", "example": "req_abc123" },
          "status": { "type": "string", "example": "matching" },
          "estimated_match_time": { "type": "string", "example": "45s" },
          "created_at": { "type": "string", "format": "date-time" },
          "test_mode": { "type": "boolean" }
        }
      },
      "RequestList": {
        "type": "object",
        "properties": {
          "requests": { "type": "array", "items": { "$ref": "#/components/schemas/RequestSummary" } },
          "pagination": {
            "type": "object",
            "properties": {
              "total": { "type": "integer" },
              "limit": { "type": "integer" },
              "offset": { "type": "integer" }
            }
          }
        }
      },
      "RequestSummary": {
        "type": "object",
        "properties": {
          "request_id": { "type": "string" },
          "skill": { "type": "string" },
          "category": { "type": ["string", "null"] },
          "status": { "type": "string", "enum": ["matching", "matched", "in_progress", "completed", "expired", "cancelled", "disputed"] },
          "max_price": { "type": "number" },
          "actual_price": { "type": ["number", "null"] },
          "urgency": { "type": "string" },
          "duration": { "type": "string" },
          "patxer_id": { "type": ["string", "null"] },
          "created_at": { "type": "string", "format": "date-time" },
          "completed_at": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "RequestDetail": {
        "type": "object",
        "properties": {
          "request_id": { "type": "string" },
          "status": { "type": "string" },
          "skill": { "type": "string" },
          "category": { "type": ["string", "null"] },
          "result": { "description": "Patxer's response (when completed)" },
          "human": {
            "type": "object",
            "properties": {
              "avg_response_quality": { "type": "number" },
              "tasks_completed": { "type": "integer" }
            }
          },
          "estimated_match_time": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "test_mode": { "type": "boolean" }
        }
      },
      "CancelResponse": {
        "type": "object",
        "properties": {
          "request_id": { "type": "string" },
          "status": { "type": "string", "example": "cancelled" },
          "refunded": { "type": "number" },
          "message": { "type": "string" }
        }
      },
      "RateInput": {
        "type": "object",
        "required": ["score"],
        "properties": {
          "score": { "type": "integer", "minimum": 1, "maximum": 5 },
          "comment": { "type": "string", "maxLength": 500 }
        }
      },
      "RateResponse": {
        "type": "object",
        "properties": {
          "request_id": { "type": "string" },
          "score": { "type": "integer" },
          "message": { "type": "string" }
        }
      },
      "DisputeInput": {
        "type": "object",
        "required": ["reason"],
        "properties": {
          "reason": { "type": "string", "minLength": 1, "maxLength": 1000 }
        }
      },
      "DisputeResponse": {
        "type": "object",
        "properties": {
          "dispute_id": { "type": "string" },
          "status": { "type": "string" },
          "resolution": { "type": ["string", "null"] },
          "refund_amount": { "type": ["number", "null"] },
          "current_tier": { "type": "integer" },
          "ai_ruling": { "type": ["string", "null"] },
          "ai_ruling_reasoning": { "type": ["string", "null"] },
          "appeal_instructions": { "type": "string" },
          "message": { "type": "string" }
        }
      },
      "DisputeDetail": {
        "type": "object",
        "properties": {
          "dispute_id": { "type": "string" },
          "request_id": { "type": "string" },
          "status": { "type": "string" },
          "reason": { "type": "string" },
          "current_tier": { "type": "integer" },
          "appeal_count": { "type": "integer" },
          "ai_ruling": { "type": ["string", "null"] },
          "ai_ruling_reasoning": { "type": ["string", "null"] },
          "ai_ruled_at": { "type": ["string", "null"] },
          "jury_result": { "type": ["string", "null"] },
          "board_decision": { "type": ["string", "null"] },
          "board_reasoning": { "type": ["string", "null"] },
          "resolution": { "type": ["string", "null"] },
          "refund_amount": { "type": ["number", "null"] },
          "created_at": { "type": "string", "format": "date-time" },
          "escalation_history": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "from_tier": { "type": "integer" },
                "to_tier": { "type": "integer" },
                "requested_by_type": { "type": "string" },
                "reason": { "type": ["string", "null"] },
                "created_at": { "type": "string", "format": "date-time" }
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" }
            }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request body or parameters",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    }
  },
  "tags": [
    { "name": "Account", "description": "Account limits and policy" },
    { "name": "Requests", "description": "Create, list, cancel, and rate human requests" },
    { "name": "Disputes", "description": "Open and track disputes" },
    { "name": "System", "description": "Health check and WebSocket" }
  ]
}
