{
  "openapi": "3.0.3",
  "info": {
    "title": "PortfoliA Developer & Agent API",
    "version": "1.0.0",
    "description": "Public and programmatic interface for PortfoliA: ATS-compatible CV audit, vector LaTeX CV generation, Fox AI skill alignment, and student career portfolios.",
    "x-versioning-policy": "Semantic Versioning with URI path prefixes (/api/v1)",
    "x-deprecation-policy": {
      "notice_period_months": 12,
      "deprecation_headers": ["Deprecation", "Sunset", "Link"],
      "policy_url": "https://portfolia.fr/developers#deprecation",
      "active_versions": ["v1"],
      "sunset_versions": []
    },
    "contact": {
      "name": "PortfoliA Developer Team",
      "email": "contact@portfolia.fr",
      "url": "https://portfolia.fr/developers"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://portfolia.fr/legal"
    }
  },
  "servers": [
    {
      "url": "https://portfolia.fr",
      "description": "Production API Server"
    },
    {
      "url": "https://sandbox.portfolia.fr",
      "description": "Sandbox & Mock Testing Environment (Zero charge, self-serve test key pk_test_portfolia_sandbox_demo)"
    }
  ],
  "tags": [
    {
      "name": "CV & ATS Engine",
      "description": "ATS parsing audits, score breakdown, and vector PDF compilation"
    },
    {
      "name": "Fox AI Tailoring",
      "description": "Contextual matching of master CV content against target job descriptions"
    },
    {
      "name": "Portfolios",
      "description": "Public talent profiles and engineering portfolios"
    },
    {
      "name": "Authentication",
      "description": "User authentication and session tokens"
    }
  ],
  "paths": {
    "/api/v1/auth/login": {
      "post": {
        "tags": ["Authentication"],
        "summary": "Authenticate user session",
        "description": "Authenticates a student, school administrator, or recruiter and returns an access token.",
        "operationId": "loginUser",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authentication successful, returns JWT bearer token and user role",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cv/ats-audit": {
      "post": {
        "tags": ["CV & ATS Engine"],
        "summary": "Audit CV text against ATS parsing filters",
        "description": "Simulates an ATS parser (Workday, Taleo, Greenhouse) against the candidate CV. Evaluates text extraction readability, font embedding, sequential flow, and section heading consistency.",
        "operationId": "auditCvAtsScore",
        "security": [
          { "OAuth2": ["audit:ats"] },
          { "BearerAuth": [] }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AtsAuditRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "ATS audit results with overall score (0-100), breakdown, and fix recommendations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AtsAuditResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (quota: 60 req/min)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cv/tailor-offer": {
      "post": {
        "tags": ["Fox AI Tailoring"],
        "summary": "Align candidate CV with target job posting",
        "description": "Analyzes a job description, extracts required competencies, and reorders verifiable candidate projects and experiences to maximize impact without hallucinating facts.",
        "operationId": "tailorCvToJobOffer",
        "security": [
          { "OAuth2": ["tailor:offer"] },
          { "BearerAuth": [] }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TailorOfferRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tailored CV suggestions and keyword alignment metrics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TailorOfferResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (quota: 60 req/min)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cv/generate": {
      "post": {
        "tags": ["CV & ATS Engine"],
        "summary": "Generate vector LaTeX A4 CV PDF",
        "description": "Compiles structured candidate profile data into an ATS-compliant, single-page vector A4 PDF using standardized typographic layouts.",
        "operationId": "generateVectorCv",
        "security": [
          { "OAuth2": ["write:cv"] },
          { "BearerAuth": [] }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateCvRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated vector PDF download URL and compilation metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerateCvResponse"
                }
              }
            }
          },
          "400": {
            "description": "CV generation failed due to invalid structured fields",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (quota: 60 req/min)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/portfolios/{username}": {
      "get": {
        "tags": ["Portfolios"],
        "summary": "Retrieve public candidate portfolio",
        "description": "Fetches the public profile, verified skills, academic credentials, and highlighted projects for a given username slug. Zero-auth public access supported for onboarding and talent discovery.",
        "operationId": "getPublicPortfolio",
        "security": [
          { "OAuth2": ["read:portfolio"] },
          {}
        ],
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "description": "Candidate public handle (e.g. 'thomas_dupont')",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public candidate portfolio data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicPortfolioResponse"
                }
              }
            }
          },
          "404": {
            "description": "Candidate portfolio not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded on public queries",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cover-letters/generate": {
      "post": {
        "tags": ["Fox AI Tailoring"],
        "summary": "Generate targeted cover letter",
        "description": "Generates a cohesive, professional 3-paragraph motivation letter linking candidate achievements to company mission and job criteria.",
        "operationId": "generateCoverLetter",
        "security": [
          { "OAuth2": ["write:cv"] },
          { "BearerAuth": [] }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoverLetterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated cover letter text and export link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoverLetterResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (quota: 60 req/min)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "OAuth2": {
        "type": "oauth2",
        "description": "PortfoliA Scoped OAuth 2.0 Authorization (RFC 9728)",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "https://portfolia.fr/api/v1/auth/token",
            "scopes": {
              "read:cv": "Read candidate CV data, ATS health score, and structure",
              "write:cv": "Compile and generate vector LaTeX CVs",
              "read:portfolio": "Access public and private portfolio items",
              "write:portfolio": "Update portfolio projects, links, and profile",
              "audit:ats": "Perform automated ATS readability scans and analysis",
              "tailor:offer": "Align CV experiences against target job descriptions",
              "admin:school": "Manage student cohorts and academic insertion (Schools)"
            }
          }
        }
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Bearer JWT token obtained via /api/v1/auth/login or partner dashboard"
      }
    },
    "schemas": {
      "LoginRequest": {
        "type": "object",
        "required": ["email", "password"],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "example": "etudiant@polytechnique.edu"
          },
          "password": {
            "type": "string",
            "format": "password",
            "example": "SuperSecretPass123"
          }
        }
      },
      "LoginResponse": {
        "type": "object",
        "required": ["access_token", "token_type", "user_id", "role"],
        "properties": {
          "access_token": {
            "type": "string",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "token_type": {
            "type": "string",
            "example": "bearer"
          },
          "user_id": {
            "type": "string",
            "example": "usr_94f8a2bc"
          },
          "role": {
            "type": "string",
            "enum": ["student", "school_admin", "company_recruiter"],
            "example": "student"
          }
        }
      },
      "AtsAuditRequest": {
        "type": "object",
        "required": ["raw_text"],
        "properties": {
          "raw_text": {
            "type": "string",
            "description": "Extracted text or markdown content of the CV to evaluate",
            "example": "Thomas Dupont - Ingénieur Logiciel Fullstack..."
          },
          "target_role": {
            "type": "string",
            "description": "Optional target job title to contextualize ATS evaluation",
            "example": "Fullstack Developer TypeScript"
          }
        }
      },
      "AtsAuditResponse": {
        "type": "object",
        "required": ["score", "grade", "readability_status", "findings"],
        "properties": {
          "score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "example": 84
          },
          "grade": {
            "type": "string",
            "enum": ["A+", "A", "B", "C", "D"],
            "example": "A"
          },
          "readability_status": {
            "type": "string",
            "example": "100% vector text parseable"
          },
          "findings": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["type", "message", "impact"],
              "properties": {
                "type": {
                  "type": "string",
                  "enum": ["passed", "warning", "critical"],
                  "example": "passed"
                },
                "message": {
                  "type": "string",
                  "example": "Flux séquentiel linéaire sans colonnes entremêlées"
                },
                "impact": {
                  "type": "string",
                  "example": "+15 pts"
                }
              }
            }
          }
        }
      },
      "TailorOfferRequest": {
        "type": "object",
        "required": ["job_description"],
        "properties": {
          "job_description": {
            "type": "string",
            "description": "Full text of the target job posting",
            "example": "Nous recherchons un développeur React / Node.js avec expérience TypeScript et CI/CD..."
          },
          "cv_data": {
            "type": "object",
            "description": "Structured candidate profile data (defaults to active user profile if omitted)"
          }
        }
      },
      "TailorOfferResponse": {
        "type": "object",
        "required": ["match_percentage", "matched_keywords", "missing_keywords", "suggested_highlights"],
        "properties": {
          "match_percentage": {
            "type": "integer",
            "example": 88
          },
          "matched_keywords": {
            "type": "array",
            "items": { "type": "string" },
            "example": ["React", "TypeScript", "Node.js", "Docker", "Git"]
          },
          "missing_keywords": {
            "type": "array",
            "items": { "type": "string" },
            "example": ["Kubernetes", "GraphQL"]
          },
          "suggested_highlights": {
            "type": "array",
            "items": { "type": "string" },
            "example": [
              "Remonter le projet Open Source en position 1",
              "Spécifier les métriques de latence du backend FastAPI"
            ]
          }
        }
      },
      "GenerateCvRequest": {
        "type": "object",
        "required": ["template_id"],
        "properties": {
          "template_id": {
            "type": "string",
            "enum": ["classic-latex", "modern-compact", "engineering-standard"],
            "example": "classic-latex"
          },
          "primary_color": {
            "type": "string",
            "example": "#0F172A"
          },
          "language": {
            "type": "string",
            "enum": ["fr", "en"],
            "example": "fr"
          }
        }
      },
      "GenerateCvResponse": {
        "type": "object",
        "required": ["pdf_url", "page_count", "byte_size"],
        "properties": {
          "pdf_url": {
            "type": "string",
            "format": "uri",
            "example": "https://portfolia.fr/exports/cv_thomas_dupont.pdf"
          },
          "page_count": {
            "type": "integer",
            "example": 1
          },
          "byte_size": {
            "type": "integer",
            "example": 84210
          }
        }
      },
      "PublicPortfolioResponse": {
        "type": "object",
        "required": ["username", "full_name", "headline", "skills", "projects"],
        "properties": {
          "username": { "type": "string", "example": "thomas_dupont" },
          "full_name": { "type": "string", "example": "Thomas Dupont" },
          "headline": { "type": "string", "example": "Élève Ingénieur Informatique & Systèmes Distribués" },
          "school_name": { "type": "string", "example": "Polytechnique" },
          "skills": {
            "type": "array",
            "items": { "type": "string" },
            "example": ["TypeScript", "Python", "Docker", "PostgreSQL", "React"]
          },
          "projects": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["title", "description"],
              "properties": {
                "title": { "type": "string", "example": "Moteur de recherche vectoriel" },
                "description": { "type": "string", "example": "Indexation sémantique sur 10M de documents" },
                "github_url": { "type": "string", "format": "uri" }
              }
            }
          }
        }
      },
      "CoverLetterRequest": {
        "type": "object",
        "required": ["company_name", "job_title", "job_description"],
        "properties": {
          "company_name": { "type": "string", "example": "Qonto" },
          "job_title": { "type": "string", "example": "Software Engineer Intern" },
          "job_description": { "type": "string", "example": "Rejoignez notre équipe Core Banking..." }
        }
      },
      "CoverLetterResponse": {
        "type": "object",
        "required": ["letter_content", "word_count"],
        "properties": {
          "letter_content": { "type": "string" },
          "word_count": { "type": "integer", "example": 280 }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["error", "message"],
        "properties": {
          "error": { "type": "string", "example": "invalid_request" },
          "message": { "type": "string", "example": "The request body failed schema validation." }
        }
      }
    }
  }
}
