{
  "openapi": "3.1.0",
  "info": {
    "title": "Aetherya API",
    "version": "0.1.0",
    "description": "Partial hand-written spec covering the 6 endpoints called by the Aetherya MCP server today. Full OpenAPI coverage will be generated from the action registry in a future release. All results represent simulated behavior of a synthetic audience — not real users.",
    "contact": {
      "url": "https://aetherya.ai"
    }
  },
  "servers": [
    {
      "url": "https://api.aetherya.ai",
      "description": "Production API (set AETHERYA_API_BASE_URL to override for local/staging)"
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Preferred auth is a short-lived workspace-bound auth.md delegated bearer token obtained through /.well-known/oauth-protected-resource and the human /claim ceremony. Legacy `aeth_live_` API keys remain supported for existing integrations."
      }
    },
    "parameters": {
      "WorkspaceId": {
        "name": "X-Aetherya-Workspace-Id",
        "in": "header",
        "required": false,
        "schema": { "type": "string" },
        "description": "Optional for auth.md delegated tokens (their human-approved workspace is bound into the token); legacy API-key calls may include this header to select a workspace."
      }
    },
    "schemas": {
      "Persona": {
        "type": "object",
        "required": ["id", "name"],
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "age": { "type": "integer" },
          "occupation": { "type": "string" },
          "location": { "type": "string" },
          "interests": {
            "type": "array",
            "items": { "type": "string" }
          }
        },
        "additionalProperties": true
      },
      "AuditJobState": {
        "type": "string",
        "enum": ["queued", "running", "completed", "failed", "cancelled", "not_found", "error"]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": false },
          "error": { "type": "string" },
          "message": { "type": "string" }
        }
      }
    }
  },
  "security": [
    { "BearerAuth": [] }
  ],
  "paths": {
    "/api/public/pricing": {
      "get": {
        "operationId": "getPublicPricing",
        "summary": "Get canonical pricing tiers",
        "description": "Returns the canonical EUR pricing catalog for all Aetherya plans. Public endpoint — no authentication required. Consumed by agents, JSON-LD, and llms-full.txt.",
        "security": [],
        "responses": {
          "200": {
            "description": "Pricing catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": { "type": "string", "example": "EUR" },
                    "lastUpdated": { "type": "string", "example": "2026-07-04" },
                    "note": { "type": "string" },
                    "tiers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "string" },
                          "name": { "type": "string" },
                          "price": { "type": ["number", "null"] },
                          "currency": { "type": "string" },
                          "billingCycle": { "type": "string", "enum": ["per-session", "monthly", "custom"] },
                          "priceDisplay": { "type": "string" },
                          "sessionAllowance": { "type": "string" },
                          "featureBullets": { "type": "array", "items": { "type": "string" } },
                          "checkoutUrlPath": { "type": "string" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/audits/run": {
      "post": {
        "operationId": "runAudit",
        "summary": "Start a behavioral-simulation audit of a URL",
        "description": "Starts an async audit job. The job runs a synthetic audience against the target URL and produces predicted UX friction, hesitation, drop-off, and messaging-clarity findings. Poll `/api/audits/jobs/{jobId}` for status.",
        "parameters": [
          { "$ref": "#/components/parameters/WorkspaceId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["target", "type"],
                "properties": {
                  "target": {
                    "type": "string",
                    "format": "uri",
                    "description": "The URL to audit."
                  },
                  "type": {
                    "type": "array",
                    "items": { "type": "string" },
                    "example": ["ux"],
                    "description": "Audit type(s). Pass `[\"ux\"]` for a behavioral UX audit."
                  },
                  "cognitiveMode": {
                    "type": "boolean",
                    "description": "Enable cognitive/behavioral persona simulation. Required to get persona-level findings.",
                    "default": false
                  },
                  "numAgents": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Number of synthetic personas to simulate."
                  },
                  "auditDepth": {
                    "type": "integer",
                    "description": "How many pages to crawl."
                  },
                  "additionalInstructions": {
                    "type": "string",
                    "description": "Plain-language focus or audience guidance for the simulation."
                  },
                  "personas": {
                    "type": "array",
                    "items": { "type": "string" },
                    "description": "Optional list of persona ids to pin the simulation to."
                  },
                  "modelTier": {
                    "type": "string",
                    "description": "Optional simulation quality tier."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Audit job started successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "jobId"],
                  "properties": {
                    "success": { "type": "boolean" },
                    "jobId": { "type": "string" },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "402": {
            "description": "Insufficient session balance.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/audits/jobs/{jobId}": {
      "get": {
        "operationId": "getAuditJob",
        "summary": "Poll the status of an audit job",
        "description": "Returns the current status of an async audit job. When `status` is `completed`, `reportId` is set and the full report can be fetched from `/api/audits/{reportId}`.",
        "parameters": [
          { "$ref": "#/components/parameters/WorkspaceId" },
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Job status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "jobId", "status"],
                  "properties": {
                    "success": { "type": "boolean" },
                    "jobId": { "type": "string" },
                    "status": { "$ref": "#/components/schemas/AuditJobState" },
                    "reportId": {
                      "type": "string",
                      "description": "Set when status is 'completed'."
                    },
                    "error": {
                      "type": "string",
                      "description": "Set when status is 'failed'."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/audits/{reportId}": {
      "get": {
        "operationId": "getAuditReport",
        "summary": "Fetch a completed audit report",
        "description": "Returns the full audit report for a completed job. The `summary` and `fullReport` fields carry the raw simulation output and are normalized by the MCP client before being returned to agents.",
        "parameters": [
          { "$ref": "#/components/parameters/WorkspaceId" },
          {
            "name": "reportId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "report"],
                  "properties": {
                    "success": { "type": "boolean" },
                    "report": {
                      "type": "object",
                      "required": ["id"],
                      "properties": {
                        "id": { "type": "string" },
                        "url": { "type": "string" },
                        "type": { "type": "string" },
                        "score": { "type": "number" },
                        "summary": {},
                        "fullReport": {},
                        "createdAt": { "type": "string", "format": "date-time" }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "404": {
            "description": "Report not found.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/personas": {
      "get": {
        "operationId": "listPersonas",
        "summary": "List synthetic audiences (personas)",
        "description": "Returns the list of synthetic personas available to the authenticated user. These are simulated audience members, not real people.",
        "parameters": [
          { "$ref": "#/components/parameters/WorkspaceId" }
        ],
        "responses": {
          "200": {
            "description": "Persona list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success"],
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Persona" }
                    },
                    "error": { "type": "string" },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/personas/create": {
      "post": {
        "operationId": "createPersonas",
        "summary": "Generate synthetic audience personas",
        "description": "Generates one or more synthetic personas. When `customTraits` or `websiteUrl` is supplied, the simulation engine generates personas matching the description. All personas are synthetic — engine-generated simulated people, not real users. This call may take up to ~110 seconds for engine-generated batches.",
        "parameters": [
          { "$ref": "#/components/parameters/WorkspaceId" }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": ["individual", "batch"],
                    "description": "Use 'individual' for one persona, 'batch' for multiple."
                  },
                  "count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 25,
                    "description": "Number of personas to generate."
                  },
                  "category": {
                    "type": "string",
                    "description": "Segment label (e.g. 'e-commerce', 'fintech')."
                  },
                  "targetAudience": {
                    "type": "string",
                    "description": "Plain-language audience description."
                  },
                  "customTraits": {
                    "type": "string",
                    "description": "Detailed audience description. Triggers engine generation (slower path)."
                  },
                  "websiteUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Optional site URL. Aetherya infers the audience from its content."
                  },
                  "market": {
                    "type": "string",
                    "description": "Target market / locale (e.g. 'US', 'DE', 'UK')."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created personas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success"],
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": {
                      "oneOf": [
                        { "$ref": "#/components/schemas/Persona" },
                        {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/Persona" }
                        }
                      ]
                    },
                    "error": { "type": "string" },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/ad-lab/run": {
      "post": {
        "operationId": "runAdLab",
        "summary": "Test copy or messaging variants with a synthetic audience",
        "description": "Runs a copy / messaging experiment against a synthetic audience and returns predicted clarity and response scores. Used by the `aetherya_test_copy` MCP tool.",
        "parameters": [
          { "$ref": "#/components/parameters/WorkspaceId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["experiment"],
                "properties": {
                  "experiment": {
                    "type": "object",
                    "required": ["name", "variants"],
                    "properties": {
                      "name": { "type": "string" },
                      "variants": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "required": ["id"],
                          "properties": {
                            "id": { "type": "string" },
                            "headline": { "type": "string" },
                            "body": { "type": "string" }
                          }
                        }
                      },
                      "audience": {
                        "type": "object",
                        "properties": {
                          "size": { "type": "integer" },
                          "segments": {
                            "type": "array",
                            "items": { "type": "string" }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Experiment result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success"],
                  "properties": {
                    "success": { "type": "boolean" },
                    "module": { "type": "string" },
                    "mode": { "type": "string" },
                    "message": { "type": "string" },
                    "node": {
                      "type": "object",
                      "properties": {
                        "status_code": { "type": "integer" },
                        "stdout": { "type": "string" },
                        "stderr": { "type": "string" }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/audience-chat/sessions": {
      "post": {
        "operationId": "createChatSession",
        "summary": "Create an audience chat session",
        "description": "Creates a conversation session with a synthetic persona. Used as the first step of the `aetherya_ask_audience` MCP tool — follow with a POST to `/api/audience-chat/message`.",
        "parameters": [
          { "$ref": "#/components/parameters/WorkspaceId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["mode"],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": ["persona", "group"],
                    "description": "Target a single persona or a group."
                  },
                  "targetPersonaId": {
                    "type": "string",
                    "description": "The persona id to chat with (required when mode is 'persona')."
                  },
                  "targetGroupId": {
                    "type": "string",
                    "description": "The group id to chat with (required when mode is 'group')."
                  },
                  "title": { "type": "string" },
                  "chatContext": {
                    "type": "string",
                    "description": "Background/situation for the persona to consider."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success"],
                  "properties": {
                    "success": { "type": "boolean" },
                    "data": {
                      "type": "object",
                      "required": ["id"],
                      "properties": {
                        "id": { "type": "string" }
                      },
                      "additionalProperties": true
                    },
                    "error": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/audience-chat/message": {
      "post": {
        "operationId": "sendChatMessage",
        "summary": "Send a message to a synthetic audience chat session",
        "description": "Sends a message to an existing audience chat session and returns the synthetic persona's predicted response. This is the second step of the `aetherya_ask_audience` MCP tool.",
        "parameters": [
          { "$ref": "#/components/parameters/WorkspaceId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["sessionId", "message"],
                "properties": {
                  "sessionId": {
                    "type": "string",
                    "description": "The session id returned by POST /api/audience-chat/sessions."
                  },
                  "message": {
                    "type": "string",
                    "description": "The question or message to send to the synthetic audience."
                  },
                  "chatContext": {
                    "type": "string",
                    "description": "Optional additional context for this message."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Persona response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "reply"],
                  "properties": {
                    "success": { "type": "boolean" },
                    "mode": { "type": "string" },
                    "reply": {
                      "type": "string",
                      "description": "The synthetic persona's predicted response."
                    },
                    "innerThoughts": {
                      "type": "string",
                      "description": "Optional inner-monologue reasoning from the persona (may not always be present)."
                    },
                    "sessionId": { "type": "string" },
                    "messageId": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    }
  }
}
