{
  "openapi": "3.0.3",
  "info": {
    "title": "Finetune Resume API",
    "version": "1.0.0",
    "description": "AI-powered resume tailoring API. Upload your base resume once, then generate optimized, ATS-friendly versions for every job description via API.",
    "contact": {
      "email": "support@finetuneresume.app",
      "url": "https://finetuneresume.app/developers/docs"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.finetuneresume.app/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Resumes",
      "description": "Generate and manage tailored resumes"
    },
    {
      "name": "Usage",
      "description": "Usage statistics and credit balance"
    },
    {
      "name": "API Keys",
      "description": "Manage API keys"
    }
  ],
  "paths": {
    "/resumes/generate": {
      "post": {
        "operationId": "generateResume",
        "summary": "Generate a tailored resume",
        "description": "Tailor your base resume to a specific job description using AI. Deducts credits based on the finetune level.",
        "tags": ["Resumes"],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Unique string to prevent duplicate processing. Keys expire after 24 hours.",
            "schema": {
              "type": "string",
              "maxLength": 256
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateResumeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resume generated successfully",
            "headers": {
              "ratelimit-limit": { "$ref": "#/components/headers/RateLimitLimit" },
              "ratelimit-remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "ratelimit-reset": { "$ref": "#/components/headers/RateLimitReset" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Resume"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/MissingApiKey" },
          "403": { "$ref": "#/components/responses/InvalidApiKey" },
          "429": { "$ref": "#/components/responses/RateLimitExceeded" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/resumes": {
      "get": {
        "operationId": "listResumes",
        "summary": "List generated resumes",
        "description": "Returns a paginated list of your generated resumes, newest first.",
        "tags": ["Resumes"],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Number of results to return."
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "Cursor for next page. Pass the id of the last item."
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "Cursor for previous page. Pass the id of the first item."
          }
        ],
        "responses": {
          "200": {
            "description": "List of resumes",
            "headers": {
              "ratelimit-limit": { "$ref": "#/components/headers/RateLimitLimit" },
              "ratelimit-remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
              "ratelimit-reset": { "$ref": "#/components/headers/RateLimitReset" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResumeList"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/MissingApiKey" },
          "403": { "$ref": "#/components/responses/InvalidApiKey" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/resumes/{id}": {
      "get": {
        "operationId": "getResume",
        "summary": "Get a specific resume",
        "description": "Retrieve a specific resume with full resume data.",
        "tags": ["Resumes"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "description": "Resume ID (e.g. res_a1b2c3d4e5f6)"
          }
        ],
        "responses": {
          "200": {
            "description": "Resume details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Resume"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/MissingApiKey" },
          "403": { "$ref": "#/components/responses/InvalidApiKey" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      },
      "delete": {
        "operationId": "deleteResume",
        "summary": "Delete a resume",
        "description": "Permanently delete a generated resume.",
        "tags": ["Resumes"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "description": "Resume ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Resume deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedResume"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/MissingApiKey" },
          "403": { "$ref": "#/components/responses/InvalidApiKey" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/resumes/{id}/pdf": {
      "post": {
        "operationId": "generatePdf",
        "summary": "Generate PDF for a resume",
        "description": "Generate or regenerate a PDF for an existing tailored resume.",
        "tags": ["Resumes"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "description": "Resume ID"
          }
        ],
        "responses": {
          "200": {
            "description": "PDF generated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResumePdf"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/MissingApiKey" },
          "403": { "$ref": "#/components/responses/InvalidApiKey" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Get usage statistics",
        "description": "Returns your current credit balance, usage stats, and rate limit info.",
        "tags": ["Usage"],
        "responses": {
          "200": {
            "description": "Usage statistics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/MissingApiKey" },
          "403": { "$ref": "#/components/responses/InvalidApiKey" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    },
    "/api-keys": {
      "get": {
        "operationId": "listApiKeys",
        "summary": "List API keys",
        "description": "List your API keys. Secret values are masked.",
        "tags": ["API Keys"],
        "responses": {
          "200": {
            "description": "List of API keys",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/MissingApiKey" },
          "403": { "$ref": "#/components/responses/InvalidApiKey" },
          "500": { "$ref": "#/components/responses/InternalServerError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key. Use ft_live_* for production, ft_test_* for test mode."
      }
    },
    "headers": {
      "RateLimitLimit": {
        "description": "Max requests per minute for your plan",
        "schema": { "type": "integer", "example": 60 }
      },
      "RateLimitRemaining": {
        "description": "Requests remaining in current window",
        "schema": { "type": "integer", "example": 58 }
      },
      "RateLimitReset": {
        "description": "Unix timestamp when the rate limit window resets",
        "schema": { "type": "integer", "example": 1740130260 }
      }
    },
    "schemas": {
      "GenerateResumeRequest": {
        "type": "object",
        "required": ["job_description", "company_name", "position"],
        "properties": {
          "job_description": {
            "type": "string",
            "minLength": 50,
            "description": "Full job description text."
          },
          "company_name": {
            "type": "string",
            "description": "Target company name."
          },
          "position": {
            "type": "string",
            "description": "Target job title."
          },
          "finetune_level": {
            "type": "string",
            "enum": ["basic", "good", "super"],
            "default": "good",
            "description": "How aggressively to tailor the resume. basic=keyword optimization, good=balanced rewrite, super=deep creative rewrite."
          },
          "include_pdf": {
            "type": "boolean",
            "default": false,
            "description": "Generate a PDF immediately along with the resume data."
          }
        }
      },
      "Resume": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": ["resume"],
            "description": "Object type identifier."
          },
          "id": {
            "type": "string",
            "description": "Unique resume ID.",
            "example": "res_a1b2c3d4e5f6"
          },
          "company_name": {
            "type": "string",
            "example": "Google"
          },
          "position": {
            "type": "string",
            "example": "Senior Software Engineer"
          },
          "finetune_level": {
            "type": "string",
            "enum": ["basic", "good", "super"]
          },
          "ats_score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "ATS compatibility score (0-100)."
          },
          "resume_data": {
            "type": "object",
            "description": "Full resume data in Reactive Resume JSON format.",
            "properties": {
              "basics": {
                "type": "object",
                "description": "Name, headline, contact info, etc."
              },
              "sections": {
                "type": "object",
                "description": "Resume sections: summary, experience, skills, education, etc."
              }
            }
          },
          "pdf_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "URL to the generated PDF. Null if PDF was not requested."
          },
          "credits_remaining": {
            "type": "integer",
            "description": "Credits remaining after this generation."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ResumeList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": ["list"]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Resume"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether there are more results available."
          }
        }
      },
      "ResumePdf": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": ["resume.pdf"]
          },
          "id": {
            "type": "string"
          },
          "pdf_url": {
            "type": "string",
            "format": "uri"
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DeletedResume": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": ["resume.deleted"]
          },
          "id": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean",
            "enum": [true]
          }
        }
      },
      "Usage": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": ["usage"]
          },
          "credits_total": {
            "type": "integer"
          },
          "credits_used": {
            "type": "integer"
          },
          "credits_remaining": {
            "type": "integer"
          },
          "resumes_generated_this_week": {
            "type": "integer"
          },
          "plan": {
            "type": "string",
            "enum": ["free", "pro"]
          },
          "rate_limit": {
            "type": "object",
            "properties": {
              "requests_per_minute": { "type": "integer" },
              "remaining": { "type": "integer" }
            }
          },
          "billing_cycle_end": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": ["api_key"]
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "Masked key prefix for identification."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ApiKeyList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": ["list"]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            }
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": ["error"]
          },
          "name": {
            "type": "string",
            "description": "Machine-readable error code.",
            "enum": [
              "validation_error",
              "missing_api_key",
              "invalid_api_key",
              "not_found",
              "rate_limit_exceeded",
              "credit_limit_exceeded",
              "internal_server_error"
            ]
          },
          "message": {
            "type": "string",
            "description": "Human-readable error message."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code."
          }
        }
      }
    },
    "responses": {
      "ValidationError": {
        "description": "Invalid or missing request parameters",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "object": "error",
              "name": "validation_error",
              "message": "job_description must be at least 50 characters",
              "status": 400
            }
          }
        }
      },
      "MissingApiKey": {
        "description": "No X-API-Key header provided",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "object": "error",
              "name": "missing_api_key",
              "message": "Provide your API key in the X-API-Key header",
              "status": 401
            }
          }
        }
      },
      "InvalidApiKey": {
        "description": "API key is invalid, revoked, or expired",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "object": "error",
              "name": "invalid_api_key",
              "message": "The provided API key is invalid or has been revoked",
              "status": 403
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "object": "error",
              "name": "not_found",
              "message": "Resume not found",
              "status": 404
            }
          }
        }
      },
      "RateLimitExceeded": {
        "description": "Rate limit or credit limit exceeded",
        "headers": {
          "ratelimit-limit": { "$ref": "#/components/headers/RateLimitLimit" },
          "ratelimit-remaining": { "$ref": "#/components/headers/RateLimitRemaining" },
          "ratelimit-reset": { "$ref": "#/components/headers/RateLimitReset" }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "object": "error",
              "name": "rate_limit_exceeded",
              "message": "Rate limit exceeded. Retry after 2026-02-21T10:31:00Z",
              "status": 429
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Internal server error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "object": "error",
              "name": "internal_server_error",
              "message": "An unexpected error occurred. Please retry with an idempotency key.",
              "status": 500
            }
          }
        }
      }
    }
  }
}
