{
  "openapi": "3.1.0",
  "info": {
    "title": "Shortlisted API",
    "version": "1.0.0",
    "description": "Public API for Shortlisted — AI-powered resume tailoring, cover letter generation, and job matching. Designed for use by LLM agents (ChatGPT Custom GPTs, Claude tool use, Anthropic Computer Use, MCP clients) and third-party applications.\n\nAuthentication: session cookie `jobsearch_session` set after magic-link verification, or `Authorization: Bearer <session_id>` header.\n\nUse the chat UI at https://shortlisted.site to onboard end users and obtain a session.",
    "contact": { "name": "Shortlisted", "url": "https://shortlisted.site" },
    "license": { "name": "Proprietary" }
  },
  "servers": [
    { "url": "https://jobagent-ten.vercel.app", "description": "Production" }
  ],
  "tags": [
    { "name": "resume", "description": "Build, tailor, and download resumes" },
    { "name": "cover-letter", "description": "Generate cover letters" },
    { "name": "profile", "description": "User profile and resume text" },
    { "name": "match", "description": "Score a resume against a job description" },
    { "name": "jobs", "description": "Search and read job listings" },
    { "name": "auth", "description": "Magic-link authentication" }
  ],
  "security": [{ "sessionCookie": [] }, { "bearerAuth": [] }],
  "paths": {
    "/api/auth/magic-link": {
      "post": {
        "tags": ["auth"],
        "summary": "Send a magic-link login email",
        "description": "Initiates a passwordless login. The user will receive an email with a link that, when clicked, sets the session cookie.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email"],
                "properties": { "email": { "type": "string", "format": "email" } }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Magic link sent" },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/api/auth/me": {
      "get": {
        "tags": ["auth"],
        "summary": "Get the current authenticated user",
        "responses": {
          "200": {
            "description": "Current user",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/User" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/profile": {
      "get": {
        "tags": ["profile"],
        "summary": "Get the current user's profile",
        "responses": {
          "200": {
            "description": "Profile",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Profile" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      },
      "put": {
        "tags": ["profile"],
        "summary": "Update the current user's profile",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ProfileUpdate" }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated profile" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/profile/resume": {
      "post": {
        "tags": ["profile"],
        "summary": "Upload a resume file (PDF or DOCX)",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["resume"],
                "properties": {
                  "resume": { "type": "string", "format": "binary", "description": "PDF or DOCX, max 10MB" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Parsed resume",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resumeText": { "type": "string" },
                    "skills": { "type": "array", "items": { "type": "string" } }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      },
      "delete": {
        "tags": ["profile"],
        "summary": "Delete the user's resume",
        "responses": {
          "200": { "description": "Deleted" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/profile/generate-resume": {
      "post": {
        "tags": ["resume"],
        "summary": "Generate an AI-tailored resume",
        "description": "Generates a resume tailored to an optional job description, returned as a PDF stream or JSON. If `jobId` and `rewrite=true` are supplied, the resume is rewritten for ATS optimization against that job. Counts against the user's monthly resume_download quota.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/GenerateResumeRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resume PDF (when format=pdf) or JSON",
            "content": {
              "application/pdf": { "schema": { "type": "string", "format": "binary" } },
              "application/json": {
                "schema": { "$ref": "#/components/schemas/GenerateResumeResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/UsageLimitReached" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/profile/generate-cover-letter": {
      "post": {
        "tags": ["cover-letter"],
        "summary": "Generate an AI cover letter for a job",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/GenerateCoverLetterRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cover letter text",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "coverLetter": { "type": "string" },
                    "tone": { "type": "string" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/UsageLimitReached" }
        }
      }
    },
    "/api/profile/resume/match/{jobId}": {
      "post": {
        "tags": ["match"],
        "summary": "Score the user's resume against a job",
        "parameters": [
          { "name": "jobId", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Match score and suggestions",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MatchResult" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "description": "Job not found" }
        }
      }
    },
    "/api/jobs": {
      "get": {
        "tags": ["jobs"],
        "summary": "List jobs with optional filters",
        "parameters": [
          { "name": "q", "in": "query", "schema": { "type": "string" }, "description": "Free-text query" },
          { "name": "role", "in": "query", "schema": { "type": "string" } },
          { "name": "geo", "in": "query", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 25, "maximum": 100 } }
        ],
        "responses": {
          "200": {
            "description": "Jobs list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": { "type": "array", "items": { "$ref": "#/components/schemas/Job" } },
                    "total": { "type": "integer" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/jobs/{id}": {
      "get": {
        "tags": ["jobs"],
        "summary": "Get one job by id",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Job" }
              }
            }
          },
          "404": { "description": "Not found" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "sessionCookie": { "type": "apiKey", "in": "cookie", "name": "jobsearch_session" },
      "bearerAuth": { "type": "http", "scheme": "bearer" }
    },
    "schemas": {
      "User": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "name": { "type": "string" },
          "tier": { "type": "string", "enum": ["free", "pro"] }
        }
      },
      "Profile": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "email": { "type": "string" },
          "role": { "type": "string" },
          "geography": { "type": "string" },
          "salaryMin": { "type": "integer" },
          "salaryMax": { "type": "integer" },
          "skills": { "type": "array", "items": { "type": "string" } },
          "resumeText": { "type": "string", "description": "Plain-text resume (may be long)" }
        }
      },
      "ProfileUpdate": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "role": { "type": "string" },
          "geography": { "type": "string" },
          "salaryMin": { "type": "integer" },
          "salaryMax": { "type": "integer" },
          "skills": { "type": "array", "items": { "type": "string" } }
        }
      },
      "GenerateResumeRequest": {
        "type": "object",
        "properties": {
          "template": {
            "type": "string",
            "enum": ["professional", "modern", "minimal", "classic"],
            "default": "professional"
          },
          "format": { "type": "string", "enum": ["pdf", "json"], "default": "pdf" },
          "jobId": { "type": "string", "description": "If provided with rewrite=true, resume is ATS-tailored to this job" },
          "rewrite": { "type": "boolean", "default": false, "description": "Rewrite resume for ATS optimization against the job" },
          "additionalContext": { "type": "string", "description": "Free-text user notes (capped server-side)" },
          "resumeText": { "type": "string", "description": "Override resume text (skips ATS rewrite if pre-tailored)" }
        }
      },
      "GenerateResumeResponse": {
        "type": "object",
        "properties": {
          "html": { "type": "string" },
          "atsOptimization": {
            "type": "object",
            "properties": {
              "scoreBefore": { "type": "number" },
              "scoreAfter": { "type": "number" },
              "estimatedATSScore": { "type": "number" },
              "keywordsAdded": { "type": "array", "items": { "type": "string" } },
              "keywordsImproved": { "type": "array", "items": { "type": "string" } },
              "naturalnessScore": { "type": "number" },
              "warnings": { "type": "array", "items": { "type": "string" } },
              "changes": { "type": "array", "items": { "type": "string" } }
            }
          }
        }
      },
      "GenerateCoverLetterRequest": {
        "type": "object",
        "required": ["jobId"],
        "properties": {
          "jobId": { "type": "string" },
          "tone": { "type": "string", "enum": ["professional", "casual", "formal"], "default": "professional" },
          "additionalContext": { "type": "string" }
        }
      },
      "MatchResult": {
        "type": "object",
        "properties": {
          "score": { "type": "number", "minimum": 0, "maximum": 100 },
          "strengths": { "type": "array", "items": { "type": "string" } },
          "gaps": { "type": "array", "items": { "type": "string" } },
          "suggestions": { "type": "array", "items": { "type": "string" } }
        }
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "title": { "type": "string" },
          "company": { "type": "string" },
          "location": { "type": "string" },
          "remote": { "type": "boolean" },
          "salaryMin": { "type": "integer" },
          "salaryMax": { "type": "integer" },
          "description": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "source": { "type": "string" },
          "postedAt": { "type": "string", "format": "date-time" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" },
          "code": { "type": "string" }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad request",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unauthorized": {
        "description": "Authentication required",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "UsageLimitReached": {
        "description": "Monthly usage limit reached",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                { "$ref": "#/components/schemas/Error" },
                {
                  "type": "object",
                  "properties": {
                    "currentUsage": { "type": "integer" },
                    "limit": { "type": "integer" },
                    "tier": { "type": "string" }
                  }
                }
              ]
            }
          }
        }
      },
      "ServerError": {
        "description": "Server error",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
