{
  "openapi": "3.1.0",
  "info": {
    "title": "AIXO API",
    "version": "1.0.0",
    "description": "HTTP API for the AIXO studio. URL path versioning: /api/v1 is current. Breaking changes ship as /api/v2 and send Deprecation plus Sunset headers for 90 days. Auth: Sanctum Bearer. See https://aixo.it/auth.md and https://aixo.it/developers",
    "contact": {
      "name": "AIXO",
      "email": "hello@aixo.it",
      "url": "https://aixo.it/contact"
    },
    "x-deprecation-policy": "Deprecated versions send Deprecation and Sunset headers for 90 days before removal.",
    "x-api-version": "v1",
    "x-sunset-policy": "When /api/v1 is deprecated, responses include Deprecation: true and Sunset: <RFC 7231 date> for 90 days before removal."
  },
  "servers": [
    { "url": "https://aixo.it", "description": "Production API (current: /api/v1)" },
    { "url": "https://aixo.it/api/v1/sandbox", "description": "Sandbox surface (read-only metadata, no side effects)" }
  ],
  "tags": [
    { "name": "public" },
    { "name": "auth" },
    { "name": "jobs" }
  ],
  "paths": {
    "/api/signal/scan": {
      "post": {
        "operationId": "scanAgentReadiness",
        "tags": ["public"],
        "summary": "Scan a public website for agent readiness",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": { "url": { "type": "string", "example": "aixo.it" } }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Live score, four dimensions, potential score, and prioritized issues" },
          "422": { "description": "Invalid, private, or unreachable target" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1": {
      "get": {
        "operationId": "discoverV1",
        "tags": ["public"],
        "summary": "API v1 discovery",
        "responses": {
          "200": {
            "description": "Discovery document",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Discovery" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/sandbox": {
      "get": {
        "operationId": "sandboxStatusV1",
        "tags": ["public"],
        "summary": "Sandbox environment status (no side effects)",
        "responses": {
          "200": {
            "description": "Sandbox ready",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["sandbox", "status"],
                  "properties": {
                    "sandbox": { "type": "boolean", "const": true },
                    "status": { "type": "string", "enum": ["ready"] },
                    "contact": { "type": "string" },
                    "header": { "type": "string" },
                    "docs": { "type": "string", "format": "uri" }
                  }
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/projects": {
      "get": {
        "operationId": "listProjectsV1",
        "tags": ["public"],
        "summary": "List public portfolio projects (cursor pagination)",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 } },
          { "name": "cursor", "in": "query", "schema": { "type": "string" }, "description": "Opaque cursor from pagination.next_cursor" }
        ],
        "responses": {
          "200": {
            "description": "Project page",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ProjectPage" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/posts": {
      "get": {
        "operationId": "listPostsV1",
        "tags": ["public"],
        "summary": "List published posts (cursor pagination)",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 } },
          { "name": "cursor", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Post page",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PostPage" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/contact": {
      "post": {
        "operationId": "createContactV1",
        "tags": ["public"],
        "summary": "Send a project inquiry (async 202)",
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" },
          { "$ref": "#/components/parameters/SandboxHeader" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactRequest" }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted; poll is not required. Location may point to /contact.",
            "headers": {
              "Location": { "schema": { "type": "string", "format": "uri" } }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Accepted" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" },
          "422": { "$ref": "#/components/responses/Problem" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "502": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/sandbox/contact": {
      "post": {
        "operationId": "sandboxContactV1",
        "tags": ["public"],
        "summary": "Sandbox contact: same schema, no email sent",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ContactRequest" }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Sandbox accepted",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Accepted" }
              }
            }
          },
          "422": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/batch": {
      "post": {
        "operationId": "batchV1",
        "tags": ["public"],
        "summary": "Submit a list of operations in one request",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BatchRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-operation results",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BatchResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/jobs": {
      "post": {
        "operationId": "createJobV1",
        "tags": ["jobs"],
        "summary": "Start an async job (202 + Location)",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": { "type": "string", "example": "quote" }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Processing",
            "headers": {
              "Location": { "schema": { "type": "string", "format": "uri" } }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Job" }
              }
            }
          }
        }
      }
    },
    "/api/v1/jobs/{job}": {
      "get": {
        "operationId": "getJobV1",
        "tags": ["jobs"],
        "summary": "Poll async job status",
        "parameters": [
          { "name": "job", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Job status",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Job" }
              }
            }
          },
          "404": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/register": {
      "post": {
        "operationId": "register",
        "tags": ["auth"],
        "summary": "Self-serve registration; returns a Sanctum token",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name", "email", "password"],
                "properties": {
                  "name": { "type": "string" },
                  "email": { "type": "string", "format": "email" },
                  "password": { "type": "string", "minLength": 8 }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AuthSession" }
              }
            }
          },
          "422": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/login": {
      "post": {
        "operationId": "login",
        "tags": ["auth"],
        "summary": "Password login; returns a Sanctum token",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email", "password"],
                "properties": {
                  "email": { "type": "string", "format": "email" },
                  "password": { "type": "string" },
                  "remember": { "type": "boolean" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bearer token",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AuthSession" }
              }
            }
          },
          "422": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/user": {
      "get": {
        "operationId": "currentUser",
        "tags": ["auth"],
        "summary": "Current user",
        "security": [{ "bearerAuth": [], "oauth2": ["profile"] }],
        "responses": {
          "200": {
            "description": "User",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/User" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "operationId": "oauthToken",
        "tags": ["auth"],
        "summary": "OAuth token endpoint (password grant)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["grant_type", "username", "password"],
                "properties": {
                  "grant_type": { "type": "string", "enum": ["password"] },
                  "username": { "type": "string" },
                  "password": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access token",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OAuthToken" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/checkout-sessions": {
      "post": {
        "operationId": "createCheckoutSession",
        "tags": ["public"],
        "summary": "UCP checkout session",
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" },
          {
            "name": "UCP-Agent",
            "in": "header",
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "line_items": { "type": "array", "items": { "type": "object" } }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkout session",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CheckoutSession" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Sanctum",
        "description": "Sanctum token from POST /api/login or /oauth/token"
      },
      "oauth2": {
        "type": "oauth2",
        "flows": {
          "password": {
            "tokenUrl": "https://aixo.it/oauth/token",
            "scopes": {
              "profile": "Read user profile",
              "progenia": "Use Progenia chat and exports",
              "studio": "Studio dashboard and admin APIs"
            }
          }
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Replay-safe key for POST/PUT/PATCH. Cached 24h.",
        "schema": { "type": "string", "maxLength": 255 }
      },
      "SandboxHeader": {
        "name": "X-Aixo-Sandbox",
        "in": "header",
        "schema": { "type": "string", "enum": ["true"] }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["code", "message"],
        "properties": {
          "code": { "type": "string" },
          "message": { "type": "string" }
        }
      },
      "Problem": {
        "type": "object",
        "required": ["type", "title", "status", "detail"],
        "properties": {
          "type": { "type": "string", "format": "uri" },
          "title": { "type": "string" },
          "status": { "type": "integer" },
          "detail": { "type": "string" },
          "error": { "$ref": "#/components/schemas/Error" }
        }
      },
      "Pagination": {
        "type": "object",
        "required": ["limit", "has_more"],
        "properties": {
          "limit": { "type": "integer" },
          "next_cursor": { "type": ["string", "null"] },
          "has_more": { "type": "boolean" }
        }
      },
      "Project": {
        "type": "object",
        "required": ["id", "title"],
        "properties": {
          "id": { "type": "integer" },
          "title": { "type": "string" },
          "category": { "type": ["string", "null"] },
          "description": { "type": ["string", "null"] },
          "status": { "type": ["string", "null"] }
        }
      },
      "Post": {
        "type": "object",
        "required": ["id"],
        "properties": {
          "id": { "type": "integer" },
          "title": { "type": ["string", "null"] },
          "slug": { "type": ["string", "null"] }
        }
      },
      "ProjectPage": {
        "type": "object",
        "required": ["data", "pagination"],
        "properties": {
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/Project" } },
          "pagination": { "$ref": "#/components/schemas/Pagination" }
        }
      },
      "PostPage": {
        "type": "object",
        "required": ["data", "pagination"],
        "properties": {
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/Post" } },
          "pagination": { "$ref": "#/components/schemas/Pagination" }
        }
      },
      "ContactRequest": {
        "type": "object",
        "required": ["name", "email", "message"],
        "properties": {
          "name": { "type": "string", "maxLength": 120 },
          "email": { "type": "string", "format": "email" },
          "message": { "type": "string", "minLength": 10, "maxLength": 5000 },
          "phone": { "type": "string" }
        }
      },
      "Accepted": {
        "type": "object",
        "required": ["id", "status"],
        "properties": {
          "id": { "type": "string" },
          "status": { "type": "string" },
          "sandbox": { "type": "boolean" },
          "message": { "type": "string" }
        }
      },
      "Discovery": {
        "type": "object",
        "required": ["name", "version"],
        "properties": {
          "name": { "type": "string" },
          "version": { "type": "string" },
          "deprecation": { "type": "object" },
          "docs": { "type": "string", "format": "uri" },
          "openapi": { "type": "string", "format": "uri" },
          "sandbox": { "type": "string" }
        }
      },
      "BatchRequest": {
        "type": "object",
        "required": ["operations"],
        "properties": {
          "operations": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "object",
              "properties": {
                "method": { "type": "string" },
                "path": { "type": "string" }
              }
            }
          }
        }
      },
      "BatchResponse": {
        "type": "object",
        "required": ["data"],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": { "type": "integer" },
                "method": { "type": "string" },
                "path": { "type": "string" },
                "status": { "type": "integer" },
                "error": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      },
      "Job": {
        "type": "object",
        "required": ["id", "status"],
        "properties": {
          "id": { "type": "string" },
          "status": { "type": "string", "enum": ["processing", "succeeded", "failed", "open"] },
          "type": { "type": "string" },
          "result": { "type": "object" }
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "email": { "type": "string", "format": "email" }
        }
      },
      "AuthSession": {
        "type": "object",
        "properties": {
          "user": { "$ref": "#/components/schemas/User" },
          "token": { "type": "string" }
        }
      },
      "OAuthToken": {
        "type": "object",
        "required": ["access_token", "token_type"],
        "properties": {
          "access_token": { "type": "string" },
          "token_type": { "type": "string", "enum": ["Bearer"] },
          "expires_in": { "type": "integer" },
          "user": { "$ref": "#/components/schemas/User" }
        }
      },
      "CheckoutSession": {
        "type": "object",
        "required": ["id", "status"],
        "properties": {
          "id": { "type": "string" },
          "status": { "type": "string" },
          "currency": { "type": "string" },
          "continue_url": { "type": "string", "format": "uri" }
        }
      }
    },
    "responses": {
      "Problem": {
        "description": "Typed error (RFC 9457)",
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/Problem" }
          },
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Problem" }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid bearer",
        "headers": {
          "WWW-Authenticate": { "schema": { "type": "string" } }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Problem" }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests",
        "headers": {
          "Retry-After": { "schema": { "type": "integer" } },
          "RateLimit": { "schema": { "type": "string" } }
        },
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/Problem" }
          }
        }
      }
    }
  }
}
